Files
TheLadder/content/corporateladder/events/dispatch.js
T
JesseMarkowitzandClaude Opus 5 4cab9fc41e Add promotion to Dispatch, and measure how repetitive a run is
The mailroom is no longer the whole game. Around turn 50, a player who has
built standing and either Marlene's goodwill or the nerve to ask gets offered
the Dispatch post — and can take it, take it and put Trevor up for the mailroom,
or turn it down, which is a real strategy rather than a mistake and comes back
after a cooldown.

Dispatch is eleven events of its own. The money problem is largely solved up
there and replaced by other people's days depending on yours, including the
mailroom's — which you used to be. Trevor branches on whether you recommended
him: he either runs the mailroom and has opinions about how it used to be run,
or he stays put and starts addressing you by your full title, kindly, in front
of people.

None of this is a promotion mechanic in the engine. It is
`{ path: 'stage', op: 'set', value: 'dispatch' }` on an option, since `stage`
was already how events are filtered. The reserved-write rule now allows it
narrowly — `set` only, and only to a stage that has events, both checked by the
validator, so a typo cannot strand the player in an empty pool. Wages differ by
rank through two upkeep entries gated on `stage`. The hard-times events dropped
their stage entirely: rent is due wherever you work.

The second half of this is the thing the last playtest asked for. A 183-turn
session, and no way to see how much of it was new ground.

`npm run analyze <exported-log.json>` now reports turns against distinct
situations, how often each recurred, which options were most used, which locked
gates players kept meeting, and anything they typed. The retrospective shows the
player-facing version.

It found a regression in this very change on its first run. Dispatch started
with eight events against the mailroom's nineteen, so its two floor events were
46% of every promoted run — one every three turns. Promotion was moving the
player into *thinner* content. Three more events and a weight rebalance took the
top two to 24.5%, and the heaviest recurrence from every ~3 turns to every ~5.
A test now holds that line.

Also corrected: the coverage test was asking whether an archetype ever *took* an
option, which is a property of the bot, not the pack. It had flagged
`rent_bounced.ask_trevor` as dead content when it had been offered, unlocked,
419 times across a sweep and simply never chosen. Options are now checked on
availability; events are still checked on firing.

114 tests. Reasoning in docs/DECISIONS.md §22-24.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMSFHyVPitUoosW5wyEADj
2026-09-10 05:05:01 -04:00

491 lines
17 KiB
JavaScript

// Dispatch: the second tier.
//
// Same mechanics, different pressure. The mailroom's problem was money; up here
// the money is fine and the problem is that other people's days now depend on
// yours — including the mailroom's, which you used to be.
//
// The first two events carry no conditions, no cooldown and no `once`, so this
// stage has a floor of its own. Trevor's two chains are mutually exclusive
// branches on whether you put him up for your old job.
export const dispatch = [
{
id: 'dispatch_first_day',
stage: 'dispatch',
weight: 99999,
once: true,
description:
'The Dispatch desk has three phones and a laminated card taped to it, ' +
'listing what to do when each one rings. The handwriting is Marlene\'s.',
options: [
{
id: 'read_card',
label: 'Read the card.',
result: 'It is nineteen years of somebody else\'s hard-won afternoons, in eleven lines.',
effects: [
{ path: 'skills.logistics', op: 'add', value: 3 },
{ path: 'relationships.marlene.likes_you', op: 'add', value: 2 },
{ path: 'notable', op: 'push', value: 'Found Marlene\'s handwriting taped to the Dispatch desk.' },
],
},
{
id: 'answer_everything',
label: 'Answer all three phones and work it out.',
result: 'By four o\'clock you have worked out what six of the eleven lines meant.',
effects: [
{ path: 'skills.logistics', op: 'add', value: 2 },
{ path: 'skills.negotiation', op: 'add', value: 2 },
{ path: 'reputation', op: 'add', value: -2 },
],
},
{
id: 'take_it_slow',
label: 'Let the phones ring and learn the board first.',
result: 'Two callers give up. The board, however, you now understand.',
effects: [
{ path: 'skills.logistics', op: 'add', value: 4 },
{ path: 'reputation', op: 'add', value: -3 },
],
},
],
},
{
id: 'routing_board',
stage: 'dispatch',
weight: 70,
description:
'Two runs are booked against the same van at the same hour. Both floors ' +
'have written "URGENT" on the docket, one of them twice.',
options: [
{
id: 'triage',
label: 'Work out which one is actually urgent.',
result: 'Neither. You send the van to a third job nobody had flagged at all.',
effects: [
{ path: 'skills.logistics', op: 'add', value: 3 },
{ path: 'reputation', op: 'add', value: 3 },
],
},
{
id: 'split_van',
label: 'Split the run and make both floors wait half as long.',
result: 'Both floors complain about the half they waited. Neither escalates it.',
effects: [
{ path: 'skills.logistics', op: 'add', value: 1 },
{ path: 'reputation', op: 'add', value: 1 },
],
},
{
id: 'first_come',
label: 'Whoever wrote URGENT twice can go second.',
result: 'They notice. They are the sort of floor that notices.',
effects: [
{ path: 'reputation', op: 'add', value: -2 },
{ path: 'skills.logistics', op: 'add', value: 1 },
],
},
],
},
{
id: 'late_freight',
stage: 'dispatch',
weight: 55,
description:
'A freight job has come in past the cutoff. Taking it means somebody stays, ' +
'and the somebody is either you or a person you now have the standing to ask.',
options: [
{
id: 'stay_yourself',
label: 'Stay and do it yourself.',
result: 'It takes ninety minutes. Nobody sees it, which is most of the job now.',
effects: [
{ path: 'money', op: 'add', value: 90 },
{ path: 'skills.logistics', op: 'add', value: 1 },
{ path: 'reputation', op: 'add', value: 2 },
],
},
{
id: 'ask_the_floor',
label: 'Ask the mailroom to cover it.',
requires: [{ path: 'skills.negotiation', op: '>=', value: 25 }],
result:
'They cover it. You remember precisely how this felt from the other end, ' +
'and ask anyway.',
effects: [
{ path: 'skills.negotiation', op: 'add', value: 2 },
{ path: 'reputation', op: 'add', value: -1 },
{ path: 'relationships.trevor.likes_you', op: 'add', value: -4 },
{ path: 'relationships.trevor.fears_you', op: 'add', value: 3 },
],
},
{
id: 'bounce_it',
label: 'Send it back as past cutoff.',
result: 'The cutoff exists. You have simply never seen anyone use it before.',
effects: [
{ path: 'reputation', op: 'add', value: -3 },
{ path: 'skills.negotiation', op: 'add', value: 1 },
],
},
],
},
{
id: 'mailroom_calls',
stage: 'dispatch',
weight: 60,
cooldown: 4,
description:
'The mailroom is short-handed and the bins are backing up. From up here it ' +
'is a number on a board. You know exactly what it is down there.',
options: [
{
id: 'go_down',
label: 'Go down and sort bins for an hour.',
result:
'You are a coordinator sorting bins. Two people find this funny and one ' +
'finds it reassuring.',
effects: [
{ path: 'skills.logistics', op: 'add', value: 2 },
{ path: 'reputation', op: 'add', value: 3 },
{ path: 'relationships.trevor.likes_you', op: 'add', value: 5 },
{ path: 'relationships.marlene.likes_you', op: 'add', value: 4 },
],
},
{
id: 'send_help',
label: 'Pull someone off a run to help them.',
result: 'The run is late. The bins are not. You will hear about the run.',
effects: [
{ path: 'skills.logistics', op: 'add', value: 1 },
{ path: 'reputation', op: 'add', value: -1 },
{ path: 'relationships.trevor.wants_to_help_you', op: 'add', value: 4 },
],
},
{
id: 'leave_them',
label: 'It is a number on a board.',
result: 'The number goes down by itself, eventually, the way it always did.',
effects: [
{ path: 'relationships.trevor.likes_you', op: 'add', value: -5 },
{ path: 'relationships.marlene.likes_you', op: 'add', value: -3 },
],
},
],
},
{
id: 'trevor_promoted',
stage: 'dispatch',
weight: 55,
cooldown: 5,
requires: [{ path: 'flags.recommended_trevor', op: '==', value: true }],
description:
'Trevor runs the mailroom now and has opinions about how it was run before. ' +
'Some of the opinions are about you. All of them are correct.',
options: [
{
id: 'concede',
label: 'Tell him he is right.',
result: 'He is so unprepared for this that he agrees with you about something else.',
effects: [
{ path: 'relationships.trevor.likes_you', op: 'add', value: 6 },
{ path: 'relationships.trevor.wants_to_help_you', op: 'add', value: 4 },
{ path: 'skills.negotiation', op: 'add', value: 1 },
],
},
{
id: 'back_him',
label: 'Back him upstairs when it matters.',
requires: [{ path: 'reputation', op: '>=', value: 55 }],
result: 'His changes go through. He knows exactly why they went through.',
effects: [
{ path: 'relationships.trevor.wants_to_help_you', op: 'add', value: 10 },
{ path: 'reputation', op: 'add', value: 2 },
{ path: 'notable', op: 'push', value: 'Backed Trevor\'s changes to the mailroom.' },
],
},
{
id: 'pull_rank',
label: 'Remind him whose desk the board is on.',
result: 'It works. It works the way a door works when you take it off its hinges.',
effects: [
{ path: 'relationships.trevor.fears_you', op: 'add', value: 8 },
{ path: 'relationships.trevor.likes_you', op: 'add', value: -8 },
{ path: 'reputation', op: 'add', value: -2 },
],
},
],
},
{
id: 'trevor_passed_over',
stage: 'dispatch',
weight: 55,
cooldown: 5,
requires: [{ path: 'flags.recommended_trevor', op: '==', value: false }],
description:
'Trevor is still in the mailroom and has started addressing you by your ' +
'full title. He does it in front of people. He does it kindly, which is worse.',
options: [
{
id: 'address_it',
label: 'Say the thing out loud.',
requires: [{ path: 'skills.negotiation', op: '>=', value: 25 }],
result:
'He denies it, then stops doing it. Both of you agree never to refer ' +
'to the conversation again.',
effects: [
{ path: 'skills.negotiation', op: 'add', value: 3 },
{ path: 'relationships.trevor.likes_you', op: 'add', value: 6 },
{ path: 'relationships.trevor.fears_you', op: 'add', value: 2 },
],
},
{
id: 'find_him_something',
label: 'Find him something worth doing.',
result:
'You give him the freight schedule, which is a real job. He is careful ' +
'with it, and the title stops.',
effects: [
{ path: 'relationships.trevor.likes_you', op: 'add', value: 8 },
{ path: 'relationships.trevor.wants_to_help_you', op: 'add', value: 6 },
{ path: 'reputation', op: 'add', value: 2 },
{ path: 'notable', op: 'push', value: 'Found Trevor something worth doing.' },
],
},
{
id: 'let_it_run',
label: 'Let him have it.',
result: 'He has it. It does not appear to be making him any happier.',
effects: [
{ path: 'relationships.trevor.likes_you', op: 'add', value: -3 },
{ path: 'reputation', op: 'add', value: -1 },
],
},
],
},
{
id: 'marlene_upstairs',
stage: 'dispatch',
weight: 45,
cooldown: 6,
description:
'Marlene comes up with a docket she could have sent. She looks at the three ' +
'phones for slightly too long before she says anything.',
options: [
{
id: 'ask_her',
label: 'Ask whether she ever wanted this desk.',
result:
'She says nobody offered, which is not the same as no. Then she asks ' +
'whether the third-floor bins have gone up.',
effects: [
{ path: 'relationships.marlene.likes_you', op: 'add', value: 6 },
{ path: 'relationships.marlene.wants_to_help_you', op: 'add', value: 4 },
{ path: 'notable', op: 'push', value: 'Asked Marlene whether she ever wanted the Dispatch desk.' },
],
},
{
id: 'take_the_docket',
label: 'Take the docket and get on with it.',
result: 'She goes back down. The card is still taped to the desk.',
effects: [{ path: 'skills.logistics', op: 'add', value: 1 }],
},
{
id: 'put_her_up',
label: 'Put her name up for the next post that opens.',
requires: [{ path: 'reputation', op: '>=', value: 60 }],
result:
'She does not thank you. Two days later there is a second card taped ' +
'to the desk, with the four lines she had left out.',
effects: [
{ path: 'relationships.marlene.likes_you', op: 'add', value: 10 },
{ path: 'relationships.marlene.wants_to_help_you', op: 'add', value: 10 },
{ path: 'reputation', op: 'add', value: 3 },
{ path: 'notable', op: 'push', value: 'Put Marlene up for the next post that opened.' },
],
},
],
},
{
id: 'dispatch_overtime',
stage: 'dispatch',
weight: 40,
cooldown: 6,
description:
'The quarterly stocktake needs a coordinator on site for the weekend. It ' +
'pays coordinator rates, which are new to you.',
options: [
{
id: 'take_it',
label: 'Take the weekend.',
result: 'Two days, and the rate is genuinely different. You check the slip twice.',
effects: [
{ path: 'money', op: 'add', value: 320 },
{ path: 'skills.logistics', op: 'add', value: 2 },
{ path: 'reputation', op: 'add', value: 2 },
],
},
{
id: 'send_trevor',
label: 'Offer it to the mailroom instead.',
result:
'Trevor takes it. He has never been paid that for a weekend and it ' +
'shows on Monday.',
effects: [
{ path: 'relationships.trevor.likes_you', op: 'add', value: 8 },
{ path: 'relationships.trevor.wants_to_help_you', op: 'add', value: 5 },
{ path: 'reputation', op: 'add', value: 2 },
],
},
{
id: 'pass',
label: 'Pass.',
result: 'Somebody from Finance does it and gets it wrong in an interesting way.',
effects: [],
},
],
},
{
id: 'van_breakdown',
stage: 'dispatch',
weight: 55,
cooldown: 4,
description:
'The long-wheelbase van is off the road until Thursday. Everything booked ' +
'against it is still booked against it, and the board does not know that yet.',
options: [
{
id: 'rebuild_board',
label: 'Rebuild the day around what you have.',
result:
'Four jobs move, one is dropped, and nobody upstairs ever learns the ' +
'van was off the road.',
effects: [
{ path: 'skills.logistics', op: 'add', value: 4 },
{ path: 'reputation', op: 'add', value: 3 },
],
},
{
id: 'hire_one',
label: 'Hire one in for the day.',
requires: [{ path: 'money', op: '>=', value: 200 }],
result: 'It costs what it costs. The day runs as though nothing happened.',
effects: [
{ path: 'money', op: 'add', value: -180 },
{ path: 'reputation', op: 'add', value: 2 },
{ path: 'skills.negotiation', op: 'add', value: 1 },
],
},
{
id: 'tell_them',
label: 'Tell every floor their job is late and why.',
result:
'Two floors are fine about it. The third writes an email with a ' +
'subject line that uses the word "again".',
effects: [
{ path: 'reputation', op: 'add', value: -2 },
{ path: 'skills.negotiation', op: 'add', value: 2 },
],
},
],
},
{
id: 'new_starter',
stage: 'dispatch',
weight: 50,
cooldown: 5,
description:
'There is somebody new in the mailroom. They have been handed a laminated ' +
'card with the org chart on it, and their name is not on it either.',
options: [
{
id: 'walk_them_through',
label: 'Go down and walk them through it.',
result:
'You explain the card. Then you explain it again, because that is how ' +
'it works and you know that now.',
effects: [
{ path: 'reputation', op: 'add', value: 3 },
{ path: 'relationships.marlene.likes_you', op: 'add', value: 4 },
{ path: 'skills.negotiation', op: 'add', value: 1 },
{ path: 'notable', op: 'push', value: 'Walked a new starter through the org chart.' },
],
},
{
id: 'give_advice',
label: 'Tell them what you wish somebody had told you.',
requires: [{ path: 'skills.negotiation', op: '>=', value: 30 }],
result:
'They write it down, which nobody did for you. Whether it helps is ' +
'not something you will find out.',
effects: [
{ path: 'reputation', op: 'add', value: 2 },
{ path: 'skills.negotiation', op: 'add', value: 2 },
{ path: 'notable', op: 'push', value: 'Told a new starter what nobody had told you.' },
],
},
{
id: 'leave_them_to_it',
label: 'Leave them to work it out. Everyone else did.',
result: 'They work it out. It takes them eleven days.',
effects: [{ path: 'reputation', op: 'add', value: -1 }],
},
],
},
{
id: 'finance_query',
stage: 'dispatch',
weight: 45,
cooldown: 6,
description:
'Finance has queried the courier spend. The query is three lines long and ' +
'took somebody a whole morning to write.',
options: [
{
id: 'answer_properly',
label: 'Answer it properly, with the numbers.',
result:
'The numbers are fine. Finance says so in one line, which took them ' +
'a whole afternoon.',
effects: [
{ path: 'skills.logistics', op: 'add', value: 2 },
{ path: 'reputation', op: 'add', value: 4 },
],
},
{
id: 'push_back',
label: 'Ask them what they think the alternative is.',
requires: [{ path: 'skills.negotiation', op: '>=', value: 35 }],
result:
'There is no alternative. Establishing this in writing turns out to ' +
'be worth more than the money was.',
effects: [
{ path: 'skills.negotiation', op: 'add', value: 3 },
{ path: 'reputation', op: 'add', value: 5 },
{ path: 'notable', op: 'push', value: 'Won an argument with Finance in writing.' },
],
},
{
id: 'cut_the_spend',
label: 'Cut the spend and let the service slip.',
result: 'Finance is delighted. Three floors are not, and one of them notices why.',
effects: [
{ path: 'money', op: 'add', value: 60 },
{ path: 'reputation', op: 'add', value: -4 },
],
},
],
},
];
export default dispatch;