diff --git a/README.md b/README.md
index c10707e..ecd202f 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ changing underneath a running server.
| **Bestiary** | 21 statblocks across folklore, horror and the far side |
| **Eras** | modern, antique, future, anomalous — the agency is old and never abolishes anything |
| **Icons** | 292, all generated from `tools/icons.mjs`; no external art |
-| **Starter scenario** | **PAPER WINDOW**, a six-player introductory case, importable as a single Adventure |
+| **Starter scenario** | **BLACK PLATFORM**, a six-player action starter, importable as a single Adventure |
| **Compendia** | pregens, NPCs, skills, weapons, armour, gear, vehicles, powers, tables, rules, the starter |
| **Rules journal** | 12 pages, generated from `rules.mjs` so the book cannot drift from the code |
@@ -182,7 +182,7 @@ The build runs all three guards before it writes anything:
```
check-rules: OK — 87 rules, 16 files scanned, 3 aliases + 41 constant sets checked, 275 formulas verified
check-kits: OK — 25 roles, 10 trades, 218 catalogue items, every kit key resolves, every posting can use what it carries, every loadout distinct
-check-lang: OK — en.json, 926 keys, no leaf/branch collisions
-check-behaviour: OK — 36 behavioural tests
+check-lang: OK — en.json, 927 keys, no leaf/branch collisions
+check-behaviour: OK — 38 behavioural tests
```
diff --git a/docs/REVIEW_LOG.md b/docs/REVIEW_LOG.md
index ed51e51..5fd2a31 100644
--- a/docs/REVIEW_LOG.md
+++ b/docs/REVIEW_LOG.md
@@ -5011,3 +5011,62 @@ are all going to fail teaches nothing. The extraction page now says so: narrate
only if somebody wants a clean arrival, and note at debrief that the department sent six
people to a crossing and no anchor. That is the campaign rather than an oversight — but
it is a deliberate choice a GM should be able to see, not a hole they fall into.
+
+## BLACK PLATFORM replaces PAPER WINDOW as the starter
+
+The starter is now an action case: a train running through a station whose rails were
+taken out in 1976, two engineers aboard, and eleven minutes until it comes back. Go
+down, take the platform, board, rescue, fight forward along a moving train, stop it.
+
+It is a better first case than PAPER WINDOW for the same reason it is simpler. PAPER
+WINDOW's centrepiece was a three-stage negotiation and a six-signature settlement —
+excellent, and a lot to hold in your head in hour one. BLACK PLATFORM is a straight
+line with one clue scene and no social subsystem, which also means it cannot reproduce
+R-248: **no groups, no standings, nothing to leave unwired.**
+
+**Every agent can fight, deliberately.** The scenario sets floors — a firearm at 42% or
+better, a melee weapon at 40%, Brawl 40%, Dodge 40% — so that nobody is a spectator when
+initiative is rolled on their first evening. The floors only ever RAISE a rating, so a
+posting already better at something stays better: Callum's shield comes out at 53 rather
+than the floor's 45, and Owen is still far better at engineering than at fighting.
+Verified on import, all six.
+
+The build now handles three things it could not before: **combat floors**, **extra
+weapons** issued for a case, and a scenario with **no Borrowed Authority at all** — the
+station is already shut to the public, so there is nobody to be somebody else in front
+of. That last one had been assumed to exist and would have thrown.
+
+Two new tests: every floor must name a real skill (a floor on a skill the catalogue does
+not have would put a number on the sheet that no rule could ever reach), and every
+starter agent must actually clear the fighting floors the handout promises them.
+
+## The dialogs now ask questions
+
+Sixty-two strings reworded. The dialogs were naming mechanics at people:
+
+| Before | After |
+|---|---|
+| Situational modifier % | Anything else helping or hurting? (%) |
+| Target cover | Is the target behind anything? |
+| Difficulty | How hard is it? |
+| Fire | How are you firing? |
+| Range | How far away? |
+| Light | How much can you see? |
+| Applies itself | Working for you already |
+| Difficult (÷2) | Difficult — half your skill |
+| Beyond twice the long band. The shot is impossible. | Too far away to hit. Get closer or use something with more reach. |
+| That is Impossible. No roll is permitted. | That cannot be done at any skill. Try something else. |
+
+The principle: **a label should ask the question, and a hint should say what it does to
+the number.** The difficulty ladder now says what it does — "Easy — double your skill",
+"Difficult — half your skill" — rather than printing an operator. Notifications say what
+to do next instead of what went wrong.
+
+The generator dialog got the same treatment: "Recruited as" is now "Where do they come
+from?", "Tier" is "How senior are they?", and the hand-roll option explains what it
+actually costs you — "You keep exactly what you roll — no adjusting the best one, no
+species bonus."
+
+Checked afterwards that every localisation key still referenced by the engine or a
+template exists; the six apparent misses are Handlebars concatenation prefixes rather
+than lookups.
diff --git a/lang/en.json b/lang/en.json
index 10c549c..d3c405c 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -15,11 +15,11 @@
"TYPES.Item.authority": "Authority",
"RINGBRP.Tab.Access": "Access",
"RINGBRP.New.authority": "New authority",
- "RINGBRP.Scope.nothing": "nothing",
- "RINGBRP.Scope.read": "read their own records",
- "RINGBRP.Scope.routine": "the things they do daily",
+ "RINGBRP.Scope.nothing": "nothing at all",
+ "RINGBRP.Scope.read": "look at their own records",
+ "RINGBRP.Scope.routine": "the things they do every day",
"RINGBRP.Scope.privileged": "things they could do, but rarely",
- "RINGBRP.Scope.administrative": "grant permissions to others",
+ "RINGBRP.Scope.administrative": "give other people permissions",
"RINGBRP.Authority.Holder": "Belongs to",
"RINGBRP.Authority.Role": "Their job",
"RINGBRP.Authority.Scope": "Scope",
@@ -32,12 +32,12 @@
"RINGBRP.Authority.SuspectTag": "flagged",
"RINGBRP.Authority.MergingTag": "records converging",
"RINGBRP.Authority.Using": "Using {name}",
- "RINGBRP.Authority.Asking": "What are you asking it to do?",
- "RINGBRP.Authority.Tripping": "Which of their habits are you breaking?",
- "RINGBRP.Authority.NoFlags": "Nothing is known about how they behave. Nobody is watching for it yet.",
- "RINGBRP.Authority.NoRoll": "no roll, 1 Trust",
- "RINGBRP.Authority.ARoll": "a stretch — roll for it",
- "RINGBRP.Authority.Cannot": "they could never",
+ "RINGBRP.Authority.Asking": "What are you asking them to do?",
+ "RINGBRP.Authority.Tripping": "How many of their habits are you breaking?",
+ "RINGBRP.Authority.NoFlags": "Nobody knows how this person behaves yet, so nobody is watching for anything odd.",
+ "RINGBRP.Authority.NoRoll": "no roll needed — costs 1 Trust",
+ "RINGBRP.Authority.ARoll": "beyond what they normally do — roll for it",
+ "RINGBRP.Authority.Cannot": "they could never do this",
"RINGBRP.Authority.Dossier": "Dossier",
"RINGBRP.Authority.AI": "Watched by",
"RINGBRP.Authority.Use": "Use",
@@ -158,11 +158,11 @@
"RINGBRP.Style.Hint.Second": "a second shot in the round",
"RINGBRP.Style.Hint.Move": "{n}% if you move and fire",
"RINGBRP.Style.Hint.Pierce": "ignores {n} points of armour",
- "RINGBRP.Difficulty.Automatic": "Automatic",
- "RINGBRP.Difficulty.Easy": "Easy (×2)",
- "RINGBRP.Difficulty.Average": "Average",
- "RINGBRP.Difficulty.Difficult": "Difficult (÷2)",
- "RINGBRP.Difficulty.Impossible": "Impossible",
+ "RINGBRP.Difficulty.Automatic": "Automatic — no roll needed",
+ "RINGBRP.Difficulty.Easy": "Easy — double your skill",
+ "RINGBRP.Difficulty.Average": "Average — skill as printed",
+ "RINGBRP.Difficulty.Difficult": "Difficult — half your skill",
+ "RINGBRP.Difficulty.Impossible": "Impossible — cannot be rolled",
"RINGBRP.Cover.None": "No cover",
"RINGBRP.Cover.Light": "Light cover",
"RINGBRP.Cover.Partial": "Solid partial cover",
@@ -304,18 +304,18 @@
"RINGBRP.New.discovery": "New Discovery",
"RINGBRP.New.project": "New Project",
"RINGBRP.Dialog.Roll": "Roll",
- "RINGBRP.Dialog.RollButton": "Roll",
+ "RINGBRP.Dialog.RollButton": "Roll it",
"RINGBRP.Dialog.Cancel": "Cancel",
- "RINGBRP.Dialog.Difficulty": "Difficulty",
- "RINGBRP.Dialog.Situational": "Situational modifier %",
- "RINGBRP.Dialog.OrderNote": "Difficulty multiplies the rating first, then the situational modifier is added.",
- "RINGBRP.Dialog.Distance": "Distance to target (m)",
+ "RINGBRP.Dialog.Difficulty": "How hard is it?",
+ "RINGBRP.Dialog.Situational": "Anything else helping or hurting? (%)",
+ "RINGBRP.Dialog.OrderNote": "Difficulty changes your skill first, then everything else is added or taken off.",
+ "RINGBRP.Dialog.Distance": "How far away is it? (metres)",
"RINGBRP.Dialog.Bands": "Bands",
- "RINGBRP.Dialog.Cover": "Target cover",
+ "RINGBRP.Dialog.Cover": "Is the target behind anything?",
"RINGBRP.Dialog.Moved": "I moved this round",
"RINGBRP.Dialog.Aiming": "I spent last round aiming",
- "RINGBRP.Dialog.Band": "Which range band?",
- "RINGBRP.Dialog.BandHint": "This weapon does different damage at each band.",
+ "RINGBRP.Dialog.Band": "Which range?",
+ "RINGBRP.Dialog.BandHint": "This weapon hits harder or softer depending on range.",
"RINGBRP.Attack.Moved": "Moved and fired",
"RINGBRP.Attack.Aimed": "Aimed",
"RINGBRP.Attack.IgnoresArmour": "Ignores {n} points of armour",
@@ -379,13 +379,13 @@
"RINGBRP.Expedition.WayHome": "The way home: {mod}% on every Transposition roll.{extra}",
"RINGBRP.Expedition.WayHomeDifficult": " The return is Difficult as well.",
"RINGBRP.Cross.WindowClock": "The window closed on you {n} time(s): {mod}%",
- "RINGBRP.Notify.Impossible": "That is Impossible. No roll is permitted.",
- "RINGBRP.Notify.NoSkill": "No skill on this sheet matches {name}.",
- "RINGBRP.Notify.OutOfRange": "Beyond twice the long band. The shot is impossible.",
- "RINGBRP.Notify.Empty": "Empty. Reload first.",
+ "RINGBRP.Notify.Impossible": "That cannot be done at any skill. Try something else.",
+ "RINGBRP.Notify.NoSkill": "Nothing on this sheet knows how to use {name}.",
+ "RINGBRP.Notify.OutOfRange": "Too far away to hit. Get closer or use something with more reach.",
+ "RINGBRP.Notify.Empty": "Empty. Reload before firing.",
"RINGBRP.Notify.NoLeg": "No unresolved phase remains.",
"RINGBRP.Notify.BadDamage": "Damage was not a number — the hit was not applied. See the console.",
- "RINGBRP.Notify.BlowStopped": "That blow was turned aside. There is no damage to roll.",
+ "RINGBRP.Notify.BlowStopped": "That blow was stopped. There is no damage to roll.",
"RINGBRP.Notify.MissingKit": "{role}: the catalogue has no item for {keys}. The agent was created without it.",
"RINGBRP.Notify.NoNavigator": "No agent has Tradecraft or Navigate.",
"RINGBRP.Sheet.RollCharHint": "Roll d100 against this characteristic ×5",
@@ -619,21 +619,21 @@
"RINGBRP.Gen.NewNPC": "New NPC / Threat",
"RINGBRP.Gen.Name": "Name",
"RINGBRP.Gen.NameHint": "leave blank to generate one",
- "RINGBRP.Gen.Species": "Species",
- "RINGBRP.Gen.Role": "Role",
+ "RINGBRP.Gen.Species": "Which species?",
+ "RINGBRP.Gen.Role": "Which posting?",
"RINGBRP.Gen.RoleRandomBlurb": "A posting will be drawn at random. Pick one to see what it does.",
- "RINGBRP.Gen.KeyStat": "Key characteristic",
- "RINGBRP.Gen.Trains": "Trains",
+ "RINGBRP.Gen.KeyStat": "Best characteristic",
+ "RINGBRP.Gen.Trains": "Good at",
"RINGBRP.Gen.Draws": "Draws from stores",
- "RINGBRP.Gen.Tier": "Experience",
+ "RINGBRP.Gen.Tier": "How senior are they?",
"RINGBRP.Gen.Gravity": "Home gravity",
"RINGBRP.Gen.Threat": "Threat level",
- "RINGBRP.Gen.Count": "How many",
+ "RINGBRP.Gen.Count": "How many?",
"RINGBRP.Gen.Random": "Random",
"RINGBRP.Gen.Create": "Create",
"RINGBRP.Gen.Made": "{n} created.",
"RINGBRP.Gen.Hint": "Everything is already chosen at random. Change what you care about and press Create.",
- "RINGBRP.Gen.RandomEach": "Random (rolled separately for each)",
+ "RINGBRP.Gen.RandomEach": "Random — rolled separately for each",
"RINGBRP.Install.Done": "Installed {n} items into the Items directory.",
"RINGBRP.Install.Button": "Install all content",
"RINGBRP.Sheet.FullList": "Add full skill list",
@@ -756,7 +756,7 @@
"RINGBRP.Sheet.OutOfFight": "Out of the fight",
"RINGBRP.Sheet.Overloaded": "Overloaded",
"RINGBRP.Sheet.Physical": "physical skills",
- "RINGBRP.Dialog.Range": "Range to target",
+ "RINGBRP.Dialog.Range": "How far away?",
"RINGBRP.Range.Impossible": "out of range",
"RINGBRP.Defence.dodge": "Dodge",
"RINGBRP.Defence.parry": "Parry",
@@ -764,10 +764,10 @@
"RINGBRP.Flavour.Defending": "Defence {n} this round: {mod}%",
"RINGBRP.Notify.DefenceLocked": "Already defending with {kind} this round.",
"RINGBRP.Sheet.Defend": "Defend",
- "RINGBRP.Sheet.DefendHint": "Roll a defence. The first this round is free; each one after is 30% harder.",
+ "RINGBRP.Sheet.DefendHint": "Answer the blow that was just aimed at you.",
"RINGBRP.Sheet.ThisRound": "this round",
"RINGBRP.Sheet.NextAt": "next at",
- "RINGBRP.Sheet.FirstIsFree": "first is free",
+ "RINGBRP.Sheet.FirstIsFree": "first defence is free",
"RINGBRP.Notify.NoParryWeapon": "Nothing in hand to parry with.",
"RINGBRP.Sheet.Mass": "Weight",
"RINGBRP.Folder.era.antique": "Antique",
@@ -866,12 +866,12 @@
"RINGBRP.Lighting.dim": "Dim",
"RINGBRP.Lighting.dark": "Dark",
"RINGBRP.Lighting.pitch": "Pitch dark",
- "RINGBRP.Dialog.Fire": "Fire",
- "RINGBRP.Dialog.Lighting": "Light",
- "RINGBRP.Dialog.Fitted": "Applies itself",
- "RINGBRP.Notify.NoBurst": "{name} cannot fire a burst.",
- "RINGBRP.Notify.NotEnoughCharge": "Not enough charge to hold the beam that long.",
- "RINGBRP.Notify.SecondUsed": "That second shot has already been taken this round.",
+ "RINGBRP.Dialog.Fire": "How are you firing?",
+ "RINGBRP.Dialog.Lighting": "How much can you see?",
+ "RINGBRP.Dialog.Fitted": "Working for you already",
+ "RINGBRP.Notify.NoBurst": "{name} cannot fire bursts — it only fires one shot at a time.",
+ "RINGBRP.Notify.NotEnoughCharge": "Not enough charge left to hold the beam that long.",
+ "RINGBRP.Notify.SecondUsed": "You have already taken your second shot this round.",
"RINGBRP.Chat.Damage": "damage",
"RINGBRP.Chat.Sustained": "sustained",
"RINGBRP.Chat.DamageMod": "damage mod",
@@ -879,50 +879,51 @@
"RINGBRP.Sight.LampOwn": "yours, {reach} m of it",
"RINGBRP.Sight.LampNear": "{holder}'s, {m} m away — you are in it",
"RINGBRP.Sight.SomeLamp": "The light you are standing in",
- "RINGBRP.Dialog.InLight": "I am in the light",
- "RINGBRP.Dialog.InLightHint": "Ticked already if a lit lamp is within reach of your token. Tick it yourself when there are no tokens to measure.",
+ "RINGBRP.Dialog.InLight": "I am standing in the light",
+ "RINGBRP.Dialog.InLightHint": "Already ticked if a lit lamp is close enough to your token. Tick it yourself if you are not using tokens.",
"RINGBRP.Sheet.Ready": "Ready",
- "RINGBRP.Sheet.ReadyToggle": "Ready this weapon — puts it in the strip at the top of the sheet",
- "RINGBRP.Sheet.ReadyHint": "Click the fist to ready a weapon. Readied weapons appear in the strip under the characteristics, where they can be rolled without opening this tab.",
+ "RINGBRP.Sheet.ReadyToggle": "Ready this weapon — it appears in the strip at the top of the sheet",
+ "RINGBRP.Sheet.ReadyHint": "Click the fist to ready a weapon. Readied weapons show in the strip under the characteristics, where you can roll them without opening this tab.",
"RINGBRP.Sheet.BodyHead": "The body, and where it gets hit",
- "RINGBRP.Sheet.MiniBodyHint": "Every location: damage taken, and the armour covering it.",
+ "RINGBRP.Sheet.MiniBodyHint": "Every part of the body: damage taken, and the armour covering it.",
"RINGBRP.Coverage.all": "all of you",
"RINGBRP.Coverage.torso": "torso",
"RINGBRP.Coverage.torsoArms": "torso and arms",
"RINGBRP.Coverage.torsoLimbs": "torso, arms and legs",
"RINGBRP.Coverage.head": "head",
"RINGBRP.Sheet.Unarmoured": "bare",
- "RINGBRP.Sheet.ByLocation": "by location",
+ "RINGBRP.Sheet.ByLocation": "depending where you are hit",
"RINGBRP.Item.Coverage": "Covers",
"RINGBRP.Shield.Raised": "raised — {n} points where it covers",
"RINGBRP.Shield.InTheWay": "raised — you are shooting around it ({mod}%)",
"RINGBRP.Shield.Wear": "{name} takes the blow — {n}/{max}",
"RINGBRP.Shield.Broken": "{name} comes apart. {n}/{max} — it is firewood.",
- "RINGBRP.Sheet.RaiseHint": "Raise or lower this shield. Raised it protects what it covers and you can defend with it; lowered it is weight.",
+ "RINGBRP.Sheet.RaiseHint": "Raise or lower this shield. Raised, it protects what it covers and you can defend with it; lowered, it is just weight.",
"RINGBRP.Sheet.ShieldUp": "raised",
"RINGBRP.Sheet.ShieldDown": "slung",
"RINGBRP.Sheet.ShieldBroken": "broken",
- "RINGBRP.Sheet.ShieldHint": "Take the blow on the shield. It wears against anything better than an ordinary hit.",
- "RINGBRP.Notify.ShieldBroken": "{name} is broken. It cannot be raised.",
+ "RINGBRP.Sheet.ShieldHint": "Take it on the shield. The shield wears down against anything better than an ordinary hit.",
+ "RINGBRP.Notify.ShieldBroken": "{name} is broken. It cannot be raised again.",
"RINGBRP.Item.IsShield": "Is a shield",
"RINGBRP.Item.Raised": "Raised",
"RINGBRP.Item.Wear": "Wear",
"RINGBRP.Item.WearMax": "Breaks at",
- "RINGBRP.Gen.Source": "Recruited as",
+ "RINGBRP.Gen.Source": "Where do they come from?",
"RINGBRP.Gen.SourcePosting": "A posting — career department",
"RINGBRP.Gen.SourceTrade": "A trade — recruited from an ordinary job",
- "RINGBRP.Gen.Trade": "Trade",
- "RINGBRP.Gen.PlusInduction": "Plus induction",
- "RINGBRP.Gen.PlusIssue": "Plus issue",
- "RINGBRP.Gen.HandRoll": "Roll the characteristics myself",
- "RINGBRP.Gen.HandRollHint": "Eight rolls, one at a time, to chat. Taken exactly as they fall — no nudging the key stat, no species shift.",
+ "RINGBRP.Gen.Trade": "Which job did they do before?",
+ "RINGBRP.Gen.PlusInduction": "Plus six weeks of training",
+ "RINGBRP.Gen.PlusIssue": "Plus issued kit",
+ "RINGBRP.Gen.HandRoll": "I want to roll the characteristics myself",
+ "RINGBRP.Gen.HandRollHint": "Eight rolls, one at a time, shown in chat. You keep exactly what you roll — no adjusting the best one, no species bonus.",
"RINGBRP.Gen.RollTitle": "Roll them yourself",
"RINGBRP.Gen.RollFor": "Roll them yourself — {name}",
- "RINGBRP.Gen.RollHint": "One at a time. Once a characteristic is rolled it stands.",
+ "RINGBRP.Gen.RollHint": "One at a time. Once a characteristic is rolled, it stands.",
"RINGBRP.Gen.RollIt": "Roll",
"RINGBRP.Gen.RollRest": "Roll the rest",
"RINGBRP.Gen.RolledCount": "{n} of {of} rolled",
"RINGBRP.Gen.RolledChar": "{ch}",
- "RINGBRP.Gen.TakeThem": "Take them",
- "RINGBRP.Gen.Nth": "agent {n}"
+ "RINGBRP.Gen.TakeThem": "Keep these",
+ "RINGBRP.Gen.Nth": "agent {n}",
+ "RINGBRP.Dialog.Circumstance": "How are the circumstances?"
}
diff --git a/packs/armour/000003.log b/packs/armour/000003.log
deleted file mode 100644
index 9b6d27d..0000000
Binary files a/packs/armour/000003.log and /dev/null differ
diff --git a/packs/armour/000005.ldb b/packs/armour/000005.ldb
new file mode 100644
index 0000000..c982555
Binary files /dev/null and b/packs/armour/000005.ldb differ
diff --git a/packs/armour/000006.log b/packs/armour/000006.log
new file mode 100644
index 0000000..1a1bb27
Binary files /dev/null and b/packs/armour/000006.log differ
diff --git a/packs/armour/CURRENT b/packs/armour/CURRENT
index 1a84852..cacca75 100644
--- a/packs/armour/CURRENT
+++ b/packs/armour/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/armour/LOG b/packs/armour/LOG
index 9981dfc..b150376 100644
--- a/packs/armour/LOG
+++ b/packs/armour/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.882913 16f9c7000 Delete type=3 #1
+2026/08/12-00:06:16.855265 1754e3000 Recovering log #3
+2026/08/12-00:06:16.855669 1754e3000 Level-0 table #5: started
+2026/08/12-00:06:16.855836 1754e3000 Level-0 table #5: 4591 bytes OK
+2026/08/12-00:06:16.856081 1754e3000 Delete type=0 #3
+2026/08/12-00:06:16.856115 1754e3000 Delete type=3 #2
diff --git a/packs/armour/LOG.old b/packs/armour/LOG.old
new file mode 100644
index 0000000..76d96d2
--- /dev/null
+++ b/packs/armour/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.690846 171b57000 Delete type=3 #1
diff --git a/packs/armour/MANIFEST-000002 b/packs/armour/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/armour/MANIFEST-000002 and /dev/null differ
diff --git a/packs/armour/MANIFEST-000004 b/packs/armour/MANIFEST-000004
new file mode 100644
index 0000000..9c05997
Binary files /dev/null and b/packs/armour/MANIFEST-000004 differ
diff --git a/packs/expedition/000003.log b/packs/expedition/000003.log
deleted file mode 100644
index 263295b..0000000
Binary files a/packs/expedition/000003.log and /dev/null differ
diff --git a/packs/expedition/000005.ldb b/packs/expedition/000005.ldb
new file mode 100644
index 0000000..4ff460d
Binary files /dev/null and b/packs/expedition/000005.ldb differ
diff --git a/packs/expedition/000006.log b/packs/expedition/000006.log
new file mode 100644
index 0000000..268b187
Binary files /dev/null and b/packs/expedition/000006.log differ
diff --git a/packs/expedition/CURRENT b/packs/expedition/CURRENT
index 1a84852..cacca75 100644
--- a/packs/expedition/CURRENT
+++ b/packs/expedition/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/expedition/LOG b/packs/expedition/LOG
index a9b58c6..2720361 100644
--- a/packs/expedition/LOG
+++ b/packs/expedition/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.926223 1701d3000 Delete type=3 #1
+2026/08/12-00:06:16.873912 1754e3000 Recovering log #3
+2026/08/12-00:06:16.874024 1754e3000 Level-0 table #5: started
+2026/08/12-00:06:16.874152 1754e3000 Level-0 table #5: 4129 bytes OK
+2026/08/12-00:06:16.874423 1754e3000 Delete type=0 #3
+2026/08/12-00:06:16.874466 1754e3000 Delete type=3 #2
diff --git a/packs/expedition/LOG.old b/packs/expedition/LOG.old
new file mode 100644
index 0000000..0d1e32e
--- /dev/null
+++ b/packs/expedition/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.732633 17134b000 Delete type=3 #1
diff --git a/packs/expedition/MANIFEST-000002 b/packs/expedition/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/expedition/MANIFEST-000002 and /dev/null differ
diff --git a/packs/expedition/MANIFEST-000004 b/packs/expedition/MANIFEST-000004
new file mode 100644
index 0000000..47fbcd4
Binary files /dev/null and b/packs/expedition/MANIFEST-000004 differ
diff --git a/packs/gear/000003.log b/packs/gear/000003.log
deleted file mode 100644
index 629ba6d..0000000
Binary files a/packs/gear/000003.log and /dev/null differ
diff --git a/packs/gear/000005.ldb b/packs/gear/000005.ldb
new file mode 100644
index 0000000..d9bac03
Binary files /dev/null and b/packs/gear/000005.ldb differ
diff --git a/packs/gear/000006.log b/packs/gear/000006.log
new file mode 100644
index 0000000..d1deb9b
Binary files /dev/null and b/packs/gear/000006.log differ
diff --git a/packs/gear/CURRENT b/packs/gear/CURRENT
index 1a84852..cacca75 100644
--- a/packs/gear/CURRENT
+++ b/packs/gear/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/gear/LOG b/packs/gear/LOG
index 967f2c9..d212fb2 100644
--- a/packs/gear/LOG
+++ b/packs/gear/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.885484 16e9af000 Delete type=3 #1
+2026/08/12-00:06:16.857059 174cd7000 Recovering log #3
+2026/08/12-00:06:16.857573 174cd7000 Level-0 table #5: started
+2026/08/12-00:06:16.857903 174cd7000 Level-0 table #5: 31614 bytes OK
+2026/08/12-00:06:16.858145 174cd7000 Delete type=0 #3
+2026/08/12-00:06:16.858196 174cd7000 Delete type=3 #2
diff --git a/packs/gear/LOG.old b/packs/gear/LOG.old
new file mode 100644
index 0000000..c8def97
--- /dev/null
+++ b/packs/gear/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.692998 170b3f000 Delete type=3 #1
diff --git a/packs/gear/MANIFEST-000002 b/packs/gear/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/gear/MANIFEST-000002 and /dev/null differ
diff --git a/packs/gear/MANIFEST-000004 b/packs/gear/MANIFEST-000004
new file mode 100644
index 0000000..f7cd73c
Binary files /dev/null and b/packs/gear/MANIFEST-000004 differ
diff --git a/packs/npcs/000003.log b/packs/npcs/000003.log
deleted file mode 100644
index 09b2bf6..0000000
Binary files a/packs/npcs/000003.log and /dev/null differ
diff --git a/packs/npcs/000005.ldb b/packs/npcs/000005.ldb
new file mode 100644
index 0000000..29f69de
Binary files /dev/null and b/packs/npcs/000005.ldb differ
diff --git a/packs/npcs/000006.log b/packs/npcs/000006.log
new file mode 100644
index 0000000..ac2ef10
Binary files /dev/null and b/packs/npcs/000006.log differ
diff --git a/packs/npcs/CURRENT b/packs/npcs/CURRENT
index 1a84852..cacca75 100644
--- a/packs/npcs/CURRENT
+++ b/packs/npcs/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/npcs/LOG b/packs/npcs/LOG
index 282cecf..b7730b8 100644
--- a/packs/npcs/LOG
+++ b/packs/npcs/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.901710 16f9c7000 Delete type=3 #1
+2026/08/12-00:06:16.840426 174cd7000 Recovering log #3
+2026/08/12-00:06:16.842114 174cd7000 Level-0 table #5: started
+2026/08/12-00:06:16.843749 174cd7000 Level-0 table #5: 225945 bytes OK
+2026/08/12-00:06:16.844107 174cd7000 Delete type=0 #3
+2026/08/12-00:06:16.844201 174cd7000 Delete type=3 #2
diff --git a/packs/npcs/LOG.old b/packs/npcs/LOG.old
new file mode 100644
index 0000000..0fc6a18
--- /dev/null
+++ b/packs/npcs/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.708940 170b3f000 Delete type=3 #1
diff --git a/packs/npcs/MANIFEST-000002 b/packs/npcs/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/npcs/MANIFEST-000002 and /dev/null differ
diff --git a/packs/npcs/MANIFEST-000004 b/packs/npcs/MANIFEST-000004
new file mode 100644
index 0000000..116e469
Binary files /dev/null and b/packs/npcs/MANIFEST-000004 differ
diff --git a/packs/powers/000003.log b/packs/powers/000003.log
deleted file mode 100644
index d723043..0000000
Binary files a/packs/powers/000003.log and /dev/null differ
diff --git a/packs/powers/000005.ldb b/packs/powers/000005.ldb
new file mode 100644
index 0000000..7f540c6
Binary files /dev/null and b/packs/powers/000005.ldb differ
diff --git a/packs/powers/000006.log b/packs/powers/000006.log
new file mode 100644
index 0000000..43e306f
Binary files /dev/null and b/packs/powers/000006.log differ
diff --git a/packs/powers/CURRENT b/packs/powers/CURRENT
index 1a84852..cacca75 100644
--- a/packs/powers/CURRENT
+++ b/packs/powers/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/powers/LOG b/packs/powers/LOG
index 128de7a..5981400 100644
--- a/packs/powers/LOG
+++ b/packs/powers/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.919152 1701d3000 Delete type=3 #1
+2026/08/12-00:06:16.850448 1754e3000 Recovering log #3
+2026/08/12-00:06:16.850937 1754e3000 Level-0 table #5: started
+2026/08/12-00:06:16.851096 1754e3000 Level-0 table #5: 3295 bytes OK
+2026/08/12-00:06:16.851406 1754e3000 Delete type=0 #3
+2026/08/12-00:06:16.851447 1754e3000 Delete type=3 #2
diff --git a/packs/powers/LOG.old b/packs/powers/LOG.old
new file mode 100644
index 0000000..2107343
--- /dev/null
+++ b/packs/powers/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.725493 171b57000 Delete type=3 #1
diff --git a/packs/powers/MANIFEST-000002 b/packs/powers/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/powers/MANIFEST-000002 and /dev/null differ
diff --git a/packs/powers/MANIFEST-000004 b/packs/powers/MANIFEST-000004
new file mode 100644
index 0000000..20de082
Binary files /dev/null and b/packs/powers/MANIFEST-000004 differ
diff --git a/packs/pregens/000003.log b/packs/pregens/000003.log
deleted file mode 100644
index 712264f..0000000
Binary files a/packs/pregens/000003.log and /dev/null differ
diff --git a/packs/pregens/000005.ldb b/packs/pregens/000005.ldb
new file mode 100644
index 0000000..ef0a53e
Binary files /dev/null and b/packs/pregens/000005.ldb differ
diff --git a/packs/pregens/000006.log b/packs/pregens/000006.log
new file mode 100644
index 0000000..ddbae86
Binary files /dev/null and b/packs/pregens/000006.log differ
diff --git a/packs/pregens/CURRENT b/packs/pregens/CURRENT
index 1a84852..cacca75 100644
--- a/packs/pregens/CURRENT
+++ b/packs/pregens/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/pregens/LOG b/packs/pregens/LOG
index e6ec8e9..50fb7ad 100644
--- a/packs/pregens/LOG
+++ b/packs/pregens/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.892036 16e9af000 Delete type=3 #1
+2026/08/12-00:06:16.836787 174cd7000 Recovering log #3
+2026/08/12-00:06:16.837779 174cd7000 Level-0 table #5: started
+2026/08/12-00:06:16.838453 174cd7000 Level-0 table #5: 58792 bytes OK
+2026/08/12-00:06:16.838760 174cd7000 Delete type=0 #3
+2026/08/12-00:06:16.838812 174cd7000 Delete type=3 #2
diff --git a/packs/pregens/LOG.old b/packs/pregens/LOG.old
new file mode 100644
index 0000000..967ffa0
--- /dev/null
+++ b/packs/pregens/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.699399 170b3f000 Delete type=3 #1
diff --git a/packs/pregens/MANIFEST-000002 b/packs/pregens/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/pregens/MANIFEST-000002 and /dev/null differ
diff --git a/packs/pregens/MANIFEST-000004 b/packs/pregens/MANIFEST-000004
new file mode 100644
index 0000000..fb2adca
Binary files /dev/null and b/packs/pregens/MANIFEST-000004 differ
diff --git a/packs/rules/000003.log b/packs/rules/000003.log
deleted file mode 100644
index 7bc4ddf..0000000
Binary files a/packs/rules/000003.log and /dev/null differ
diff --git a/packs/rules/000005.ldb b/packs/rules/000005.ldb
new file mode 100644
index 0000000..e88a6f7
Binary files /dev/null and b/packs/rules/000005.ldb differ
diff --git a/packs/rules/000006.log b/packs/rules/000006.log
new file mode 100644
index 0000000..2d0501f
Binary files /dev/null and b/packs/rules/000006.log differ
diff --git a/packs/rules/CURRENT b/packs/rules/CURRENT
index 1a84852..cacca75 100644
--- a/packs/rules/CURRENT
+++ b/packs/rules/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/rules/LOG b/packs/rules/LOG
index b0d13de..cb74d2a 100644
--- a/packs/rules/LOG
+++ b/packs/rules/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.924025 1701d3000 Delete type=3 #1
+2026/08/12-00:06:16.861054 174cd7000 Recovering log #3
+2026/08/12-00:06:16.861635 174cd7000 Level-0 table #5: started
+2026/08/12-00:06:16.861956 174cd7000 Level-0 table #5: 33473 bytes OK
+2026/08/12-00:06:16.862206 174cd7000 Delete type=0 #3
+2026/08/12-00:06:16.862237 174cd7000 Delete type=3 #2
diff --git a/packs/rules/LOG.old b/packs/rules/LOG.old
new file mode 100644
index 0000000..3d704dc
--- /dev/null
+++ b/packs/rules/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.729349 17134b000 Delete type=3 #1
diff --git a/packs/rules/MANIFEST-000002 b/packs/rules/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/rules/MANIFEST-000002 and /dev/null differ
diff --git a/packs/rules/MANIFEST-000004 b/packs/rules/MANIFEST-000004
new file mode 100644
index 0000000..f38776b
Binary files /dev/null and b/packs/rules/MANIFEST-000004 differ
diff --git a/packs/skills/000003.log b/packs/skills/000003.log
deleted file mode 100644
index 2a55440..0000000
Binary files a/packs/skills/000003.log and /dev/null differ
diff --git a/packs/skills/000005.ldb b/packs/skills/000005.ldb
new file mode 100644
index 0000000..eb948ea
Binary files /dev/null and b/packs/skills/000005.ldb differ
diff --git a/packs/skills/000006.log b/packs/skills/000006.log
new file mode 100644
index 0000000..1989ee1
Binary files /dev/null and b/packs/skills/000006.log differ
diff --git a/packs/skills/CURRENT b/packs/skills/CURRENT
index 1a84852..cacca75 100644
--- a/packs/skills/CURRENT
+++ b/packs/skills/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/skills/LOG b/packs/skills/LOG
index 486de35..7db8a54 100644
--- a/packs/skills/LOG
+++ b/packs/skills/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.875989 16f9c7000 Delete type=3 #1
+2026/08/12-00:06:16.846928 1744cb000 Recovering log #3
+2026/08/12-00:06:16.847099 1744cb000 Level-0 table #5: started
+2026/08/12-00:06:16.847302 1744cb000 Level-0 table #5: 7590 bytes OK
+2026/08/12-00:06:16.847547 1744cb000 Delete type=0 #3
+2026/08/12-00:06:16.847579 1744cb000 Delete type=3 #2
diff --git a/packs/skills/LOG.old b/packs/skills/LOG.old
new file mode 100644
index 0000000..98129dd
--- /dev/null
+++ b/packs/skills/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.685076 170b3f000 Delete type=3 #1
diff --git a/packs/skills/MANIFEST-000002 b/packs/skills/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/skills/MANIFEST-000002 and /dev/null differ
diff --git a/packs/skills/MANIFEST-000004 b/packs/skills/MANIFEST-000004
new file mode 100644
index 0000000..90efb97
Binary files /dev/null and b/packs/skills/MANIFEST-000004 differ
diff --git a/packs/slice/000003.log b/packs/slice/000003.log
deleted file mode 100644
index 4a86fe9..0000000
Binary files a/packs/slice/000003.log and /dev/null differ
diff --git a/packs/slice/000005.ldb b/packs/slice/000005.ldb
new file mode 100644
index 0000000..4851c77
Binary files /dev/null and b/packs/slice/000005.ldb differ
diff --git a/packs/slice/000006.log b/packs/slice/000006.log
new file mode 100644
index 0000000..c7d9b16
Binary files /dev/null and b/packs/slice/000006.log differ
diff --git a/packs/slice/CURRENT b/packs/slice/CURRENT
index 1a84852..cacca75 100644
--- a/packs/slice/CURRENT
+++ b/packs/slice/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/slice/LOG b/packs/slice/LOG
index 20c5c57..f2b4b09 100644
--- a/packs/slice/LOG
+++ b/packs/slice/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.917638 16f9c7000 Delete type=3 #1
+2026/08/12-00:06:16.870148 1744cb000 Recovering log #3
+2026/08/12-00:06:16.870587 1744cb000 Level-0 table #5: started
+2026/08/12-00:06:16.870864 1744cb000 Level-0 table #5: 16892 bytes OK
+2026/08/12-00:06:16.871211 1744cb000 Delete type=0 #3
+2026/08/12-00:06:16.871266 1744cb000 Delete type=3 #2
diff --git a/packs/slice/LOG.old b/packs/slice/LOG.old
new file mode 100644
index 0000000..e1cb79d
--- /dev/null
+++ b/packs/slice/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.723650 170b3f000 Delete type=3 #1
diff --git a/packs/slice/MANIFEST-000002 b/packs/slice/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/slice/MANIFEST-000002 and /dev/null differ
diff --git a/packs/slice/MANIFEST-000004 b/packs/slice/MANIFEST-000004
new file mode 100644
index 0000000..0552c3a
Binary files /dev/null and b/packs/slice/MANIFEST-000004 differ
diff --git a/packs/starter/000003.log b/packs/starter/000003.log
deleted file mode 100644
index 380686c..0000000
Binary files a/packs/starter/000003.log and /dev/null differ
diff --git a/packs/starter/000005.ldb b/packs/starter/000005.ldb
new file mode 100644
index 0000000..5c410fe
Binary files /dev/null and b/packs/starter/000005.ldb differ
diff --git a/packs/starter/000006.log b/packs/starter/000006.log
new file mode 100644
index 0000000..6c9b64b
Binary files /dev/null and b/packs/starter/000006.log differ
diff --git a/packs/starter/CURRENT b/packs/starter/CURRENT
index 1a84852..cacca75 100644
--- a/packs/starter/CURRENT
+++ b/packs/starter/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/starter/LOG b/packs/starter/LOG
index 8de9093..fa2bb45 100644
--- a/packs/starter/LOG
+++ b/packs/starter/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.913120 16f9c7000 Delete type=3 #1
+2026/08/12-00:06:16.864877 1744cb000 Recovering log #3
+2026/08/12-00:06:16.865947 1744cb000 Level-0 table #5: started
+2026/08/12-00:06:16.866787 1744cb000 Level-0 table #5: 82274 bytes OK
+2026/08/12-00:06:16.867102 1744cb000 Delete type=0 #3
+2026/08/12-00:06:16.867176 1744cb000 Delete type=3 #2
diff --git a/packs/starter/LOG.old b/packs/starter/LOG.old
new file mode 100644
index 0000000..4e5bd27
--- /dev/null
+++ b/packs/starter/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.720012 170333000 Delete type=3 #1
diff --git a/packs/starter/MANIFEST-000002 b/packs/starter/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/starter/MANIFEST-000002 and /dev/null differ
diff --git a/packs/starter/MANIFEST-000004 b/packs/starter/MANIFEST-000004
new file mode 100644
index 0000000..b8a7dce
Binary files /dev/null and b/packs/starter/MANIFEST-000004 differ
diff --git a/packs/tables/000003.log b/packs/tables/000003.log
deleted file mode 100644
index f125b7d..0000000
Binary files a/packs/tables/000003.log and /dev/null differ
diff --git a/packs/tables/000005.ldb b/packs/tables/000005.ldb
new file mode 100644
index 0000000..64baa1c
Binary files /dev/null and b/packs/tables/000005.ldb differ
diff --git a/packs/tables/000006.log b/packs/tables/000006.log
new file mode 100644
index 0000000..d5e06f3
Binary files /dev/null and b/packs/tables/000006.log differ
diff --git a/packs/tables/CURRENT b/packs/tables/CURRENT
index 1a84852..cacca75 100644
--- a/packs/tables/CURRENT
+++ b/packs/tables/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/tables/LOG b/packs/tables/LOG
index 22b41a9..8c1f830 100644
--- a/packs/tables/LOG
+++ b/packs/tables/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.906240 1701d3000 Delete type=3 #1
+2026/08/12-00:06:16.872191 173cbf000 Recovering log #3
+2026/08/12-00:06:16.872466 173cbf000 Level-0 table #5: started
+2026/08/12-00:06:16.872601 173cbf000 Level-0 table #5: 3739 bytes OK
+2026/08/12-00:06:16.872838 173cbf000 Delete type=0 #3
+2026/08/12-00:06:16.872870 173cbf000 Delete type=3 #2
diff --git a/packs/tables/LOG.old b/packs/tables/LOG.old
new file mode 100644
index 0000000..e0e34e8
--- /dev/null
+++ b/packs/tables/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.713770 17134b000 Delete type=3 #1
diff --git a/packs/tables/MANIFEST-000002 b/packs/tables/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/tables/MANIFEST-000002 and /dev/null differ
diff --git a/packs/tables/MANIFEST-000004 b/packs/tables/MANIFEST-000004
new file mode 100644
index 0000000..004ff30
Binary files /dev/null and b/packs/tables/MANIFEST-000004 differ
diff --git a/packs/talents/000003.log b/packs/talents/000003.log
deleted file mode 100644
index 443b7ea..0000000
Binary files a/packs/talents/000003.log and /dev/null differ
diff --git a/packs/talents/000005.ldb b/packs/talents/000005.ldb
new file mode 100644
index 0000000..fb653de
Binary files /dev/null and b/packs/talents/000005.ldb differ
diff --git a/packs/talents/000006.log b/packs/talents/000006.log
new file mode 100644
index 0000000..29003f7
Binary files /dev/null and b/packs/talents/000006.log differ
diff --git a/packs/talents/CURRENT b/packs/talents/CURRENT
index 1a84852..cacca75 100644
--- a/packs/talents/CURRENT
+++ b/packs/talents/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/talents/LOG b/packs/talents/LOG
index 5d1a776..25ac20e 100644
--- a/packs/talents/LOG
+++ b/packs/talents/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.879030 16f1bb000 Delete type=3 #1
+2026/08/12-00:06:16.848619 173cbf000 Recovering log #3
+2026/08/12-00:06:16.848765 173cbf000 Level-0 table #5: started
+2026/08/12-00:06:16.848948 173cbf000 Level-0 table #5: 5770 bytes OK
+2026/08/12-00:06:16.849247 173cbf000 Delete type=0 #3
+2026/08/12-00:06:16.849307 173cbf000 Delete type=3 #2
diff --git a/packs/talents/LOG.old b/packs/talents/LOG.old
new file mode 100644
index 0000000..fb93ea3
--- /dev/null
+++ b/packs/talents/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.687537 17134b000 Delete type=3 #1
diff --git a/packs/talents/MANIFEST-000002 b/packs/talents/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/talents/MANIFEST-000002 and /dev/null differ
diff --git a/packs/talents/MANIFEST-000004 b/packs/talents/MANIFEST-000004
new file mode 100644
index 0000000..f8c6b47
Binary files /dev/null and b/packs/talents/MANIFEST-000004 differ
diff --git a/packs/vehicles/000003.log b/packs/vehicles/000003.log
deleted file mode 100644
index 9950ce5..0000000
Binary files a/packs/vehicles/000003.log and /dev/null differ
diff --git a/packs/vehicles/000005.ldb b/packs/vehicles/000005.ldb
new file mode 100644
index 0000000..f6b2cd4
Binary files /dev/null and b/packs/vehicles/000005.ldb differ
diff --git a/packs/vehicles/000006.log b/packs/vehicles/000006.log
new file mode 100644
index 0000000..47800e2
Binary files /dev/null and b/packs/vehicles/000006.log differ
diff --git a/packs/vehicles/CURRENT b/packs/vehicles/CURRENT
index 1a84852..cacca75 100644
--- a/packs/vehicles/CURRENT
+++ b/packs/vehicles/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/vehicles/LOG b/packs/vehicles/LOG
index f261c92..0565ef4 100644
--- a/packs/vehicles/LOG
+++ b/packs/vehicles/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.887719 16e9af000 Delete type=3 #1
+2026/08/12-00:06:16.859394 1754e3000 Recovering log #3
+2026/08/12-00:06:16.859688 1754e3000 Level-0 table #5: started
+2026/08/12-00:06:16.859874 1754e3000 Level-0 table #5: 6939 bytes OK
+2026/08/12-00:06:16.860159 1754e3000 Delete type=0 #3
+2026/08/12-00:06:16.860190 1754e3000 Delete type=3 #2
diff --git a/packs/vehicles/LOG.old b/packs/vehicles/LOG.old
new file mode 100644
index 0000000..876771f
--- /dev/null
+++ b/packs/vehicles/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.694993 17134b000 Delete type=3 #1
diff --git a/packs/vehicles/MANIFEST-000002 b/packs/vehicles/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/vehicles/MANIFEST-000002 and /dev/null differ
diff --git a/packs/vehicles/MANIFEST-000004 b/packs/vehicles/MANIFEST-000004
new file mode 100644
index 0000000..d4d73b7
Binary files /dev/null and b/packs/vehicles/MANIFEST-000004 differ
diff --git a/packs/weapons/000003.log b/packs/weapons/000003.log
deleted file mode 100644
index e1488ed..0000000
Binary files a/packs/weapons/000003.log and /dev/null differ
diff --git a/packs/weapons/000005.ldb b/packs/weapons/000005.ldb
new file mode 100644
index 0000000..742beba
Binary files /dev/null and b/packs/weapons/000005.ldb differ
diff --git a/packs/weapons/000006.log b/packs/weapons/000006.log
new file mode 100644
index 0000000..4d427a6
Binary files /dev/null and b/packs/weapons/000006.log differ
diff --git a/packs/weapons/CURRENT b/packs/weapons/CURRENT
index 1a84852..cacca75 100644
--- a/packs/weapons/CURRENT
+++ b/packs/weapons/CURRENT
@@ -1 +1 @@
-MANIFEST-000002
+MANIFEST-000004
diff --git a/packs/weapons/LOG b/packs/weapons/LOG
index 8e81423..7557d5c 100644
--- a/packs/weapons/LOG
+++ b/packs/weapons/LOG
@@ -1 +1,5 @@
-2026/08/11-23:39:03.880963 16e9af000 Delete type=3 #1
+2026/08/12-00:06:16.852833 173cbf000 Recovering log #3
+2026/08/12-00:06:16.853536 173cbf000 Level-0 table #5: started
+2026/08/12-00:06:16.853822 173cbf000 Level-0 table #5: 14767 bytes OK
+2026/08/12-00:06:16.854096 173cbf000 Delete type=0 #3
+2026/08/12-00:06:16.854134 173cbf000 Delete type=3 #2
diff --git a/packs/weapons/LOG.old b/packs/weapons/LOG.old
new file mode 100644
index 0000000..e8dcdd7
--- /dev/null
+++ b/packs/weapons/LOG.old
@@ -0,0 +1 @@
+2026/08/12-00:05:23.689273 17134b000 Delete type=3 #1
diff --git a/packs/weapons/MANIFEST-000002 b/packs/weapons/MANIFEST-000002
deleted file mode 100644
index bbbc585..0000000
Binary files a/packs/weapons/MANIFEST-000002 and /dev/null differ
diff --git a/packs/weapons/MANIFEST-000004 b/packs/weapons/MANIFEST-000004
new file mode 100644
index 0000000..6a582af
Binary files /dev/null and b/packs/weapons/MANIFEST-000004 differ
diff --git a/system.json b/system.json
index aec29a2..b94b754 100644
--- a/system.json
+++ b/system.json
@@ -122,7 +122,7 @@
},
{
"name": "starter",
- "label": "The Custodians · PAPER WINDOW (starter scenario)",
+ "label": "The Custodians · BLACK PLATFORM (starter scenario)",
"path": "packs/starter",
"type": "Adventure",
"system": "ringbrp"
diff --git a/tools/build-packs.mjs b/tools/build-packs.mjs
index 25649ff..3c40e0e 100644
--- a/tools/build-packs.mjs
+++ b/tools/build-packs.mjs
@@ -504,10 +504,19 @@ await writePack("armour", ARMOURS.map(a => {
for (const k of R.support) addSkill(k, supportV);
if (isTrade) for (const k of INDUCTION.skills) addSkill(k, inducV);
addSkill("language:trade_cant", 55);
+ // Scenario floors. These only ever RAISE a rating — a posting that is already
+ // better at something stays better — so that nobody is a spectator in a fight.
+ for (const [id, floor] of Object.entries(spec.floors ?? {})) {
+ const [fam, sp = ""] = id.split(":");
+ const found = skills.find(x => x.fam === fam && x.spec === sp);
+ if (found) found.val = Math.max(found.val, floor);
+ else { skills.push({ fam, spec: sp, val: floor }); seen.add(id); }
+ }
// Kit: standing issue, the posting or trade's own, and induction for a trade.
const kitKeys = [...STANDING_ISSUE, ...R.kit,
...(isTrade ? INDUCTION.kit : []),
+ ...(spec.extraWeapons ?? []), ...(spec.extraGear ?? []),
...(TIER_ISSUE[spec.rank === "Probationary" ? "probationary" : "officer"] ?? [])];
const weapons = [], armour = [], gear = [];
for (const k of new Set(kitKeys)) {
@@ -518,7 +527,7 @@ await writePack("armour", ARMOURS.map(a => {
}
const bio = `
${R.label}, ${spec.rank}. ${R.blurb}
`
+ `Your job in this case. ${spec.spotlight}
`
- + `Your complication. ${spec.complication}
`
+ + (spec.complication ? `Your complication. ${spec.complication}
` : "")
+ `${spec.note}
`;
return { ...spec, skills, weapons, armour, gear, bio };
};
@@ -535,10 +544,11 @@ await writePack("armour", ARMOURS.map(a => {
actorDocs.push(actorDoc);
}
- // The credential, as an item rather than a paragraph in the brief.
+ // A case may carry no credential at all — BLACK PLATFORM's station is already shut
+ // to the public, so there is nobody to be somebody else in front of.
const A = S.STARTER_AUTHORITY;
- const authId = idFor(`item:${A.key}`);
- const authority = {
+ const authId = A ? idFor(`item:${A.key}`) : null;
+ const authority = A ? {
_id: authId, name: A.name, type: "authority", img: IC("cover_id") ?? ICON.gear,
system: {
description: A.description,
@@ -550,7 +560,7 @@ await writePack("armour", ARMOURS.map(a => {
burned: false, suspect: false, aiPosture: "", notes: ""
},
effects: [], ...BASE(), flags: { ringbrp: { key: A.key } }
- };
+ } : null;
// The case file, already at the scenario's opening state.
const C = S.STARTER_CASE;
@@ -614,26 +624,25 @@ await writePack("armour", ARMOURS.map(a => {
handouts.doc.ownership = { default: 2 };
for (const p of handouts.pages) p.ownership = { default: 2 };
- const advId = idFor("adventure:paper_window");
+ const advId = idFor("adventure:black_platform");
const adventure = {
_id: advId,
- name: "PAPER WINDOW — The Dead Letter Office",
+ name: "BLACK PLATFORM — The 00:17 Service",
img: IC("actor_case") ?? ICON.caseFile ?? ICON.actor,
- caption: "A six-player starter case. Four to five hours.",
+ caption: "A six-player action starter. Three to four hours.",
description:
- "Six brown envelopes with a 1987 postmark, from an office that closed in "
- + "1998. Open one and the person it is addressed to begins disappearing from the "
- + "record — not dying, just no longer filed.
"
- + "The complete starter: GM scenario, seven player handouts, six pregenerated "
- + "agents with their scenario jobs and complications, the cast, the Borrowed "
- + "Authority as a usable credential, and the case file already set to Containment 8, "
- + "Resources 8, Clearance 6 with its four phases loaded.
"
- + "It teaches the game while it is being played: Borrowed Authority at the "
- + "cordon, investigation in the cage, crossing at Window Six, READ / SIGNAL / OFFER "
- + "with the night shift, one containable fight, and a closing scene that needs all "
- + "six players.
",
+ "A train is running through a station whose rails were taken out in 1976. "
+ + "Two engineers boarded it and have not come back. It returns every eleven minutes, "
+ + "and every time it comes back there are more people on it.
"
+ + "Go down, take the platform, get aboard, find the engineers, fight forward along "
+ + "a moving train, and stop it before it reaches a terminus that does not exist in "
+ + "this world.
"
+ + "The complete starter: GM scenario, player handouts, six pregenerated agents who "
+ + "can all fight, the ticket inspectors, the Conductor, the two casualties, and the "
+ + "case file already set to Containment 9, Resources 8, Clearance 5 with its four "
+ + "phases loaded.
",
actors: actorDocs,
- items: [authority],
+ items: authority ? [authority] : [],
journal: [
{ ...gmJournal.doc, pages: gmJournal.pages },
{ ...handouts.doc, pages: handouts.pages }
@@ -643,7 +652,7 @@ await writePack("armour", ARMOURS.map(a => {
};
await writePack("starter", [[`!adventures!${advId}`, adventure]]);
- console.log(` starter PAPER WINDOW — ${actorDocs.length} actors, `
+ console.log(` starter BLACK PLATFORM — ${actorDocs.length} actors, `
+ `${S.STARTER_PAGES.length} GM pages, ${S.STARTER_HANDOUTS.length} handouts`);
}
diff --git a/tools/check-behaviour.mjs b/tools/check-behaviour.mjs
index d1eff2f..c6f6204 100644
--- a/tools/check-behaviour.mjs
+++ b/tools/check-behaviour.mjs
@@ -27,7 +27,8 @@ import { strict as assert } from "node:assert";
import * as RULES from "../rules.mjs";
import { ROLES, TRADES, INDUCTION, TIERS, TRADE_BANDS, CHARACTERISTIC_DICE }
from "../postings.mjs";
-import { STARTER_AUTHORITY, STARTER_GROUPS, STARTER_CASE } from "./scenario-starter.mjs";
+import { STARTER_AUTHORITY, STARTER_GROUPS, STARTER_CASE, STARTER_TEAM }
+ from "./scenario-starter.mjs";
let passed = 0, failed = 0;
// Async tests are awaited in order rather than fired off — a failure that lands after
@@ -194,7 +195,10 @@ test("two tiers beyond is impossible at any skill", () =>
// An unrecognised scope ranks 0, so EVERYTHING becomes impossible and nothing says so.
test("an unrecognised scope makes everything impossible", () =>
assert.equal(RULES.scopeStanceFor("operate", "routine"), "impossible"));
-test("the starter's credential names a real scope tier", async () => {
+// A scenario may carry no credential — but one that does must name a real tier, or
+// every action through it comes back "impossible" and nothing says why.
+test("the starter's credential, if it has one, names a real scope tier", () => {
+ if (!STARTER_AUTHORITY) return;
assert.ok(RULES.SCOPE_TIERS.includes(STARTER_AUTHORITY.scope),
`scope "${STARTER_AUTHORITY.scope}" is not a tier`);
});
@@ -226,6 +230,29 @@ test("starter phases name a kind the engine will read", () => {
assert.equal(STARTER_CASE.legs.at(-1).kindId, "extraction",
"the last phase must be the extraction, or the return clock never moves");
});
+// A floor naming a skill the catalogue does not have would silently create a skill
+// nobody can roll — the sheet would show it and no rule would ever reach it.
+test("every combat floor names a real skill", async () => {
+ const { SKILL_CATALOGUE } = await import("./content.mjs");
+ const known = new Set(SKILL_CATALOGUE.map(s => (s[1] ? `${s[0]}:${s[1]}` : s[0])));
+ for (const t of STARTER_TEAM) {
+ for (const id of Object.keys(t.floors ?? {})) {
+ assert.ok(known.has(id), `${t.name}: floor "${id}" is not a skill`);
+ }
+ }
+});
+// The scenario promises every agent can fight. Assert the promise rather than trust it.
+test("every starter agent can fight", () => {
+ for (const t of STARTER_TEAM) {
+ const f = t.floors ?? {};
+ const gun = Object.entries(f).find(([k]) => k.startsWith("firearm:"));
+ const melee = Object.entries(f).find(([k]) => k.startsWith("melee_weapon:"));
+ assert.ok(gun && gun[1] >= 40, `${t.name} has no usable firearm floor`);
+ assert.ok(melee && melee[1] >= 40, `${t.name} has no usable melee floor`);
+ assert.ok((f.brawl ?? 0) >= 40, `${t.name} has no usable Brawl floor`);
+ assert.ok((f.dodge ?? 0) >= 40, `${t.name} has no usable Dodge floor`);
+ }
+});
test("every starter group is usable by the contact engine", () => {
for (const g of STARTER_GROUPS) {
assert.ok(g.groupId && g.name, "a group needs an id and a name");
diff --git a/tools/check-kits.mjs b/tools/check-kits.mjs
index 0b52348..010fbaa 100644
--- a/tools/check-kits.mjs
+++ b/tools/check-kits.mjs
@@ -71,6 +71,7 @@ for (const [id, r] of Object.entries({ ...ROLES, ...TRADES })) {
{
const S = await import("./scenario-starter.mjs").catch(() => null);
const auth = S?.STARTER_AUTHORITY;
+ // A case may legitimately carry no credential; one that does must name a real tier.
if (auth && !SCOPE_TIERS.includes(auth.scope)) {
problems.push(`starter authority "${auth.name}": scope "${auth.scope}" is not a `
+ `scope tier (have: ${SCOPE_TIERS.join(", ")})`);
diff --git a/tools/scenario-starter.mjs b/tools/scenario-starter.mjs
index 693492c..f895168 100644
--- a/tools/scenario-starter.mjs
+++ b/tools/scenario-starter.mjs
@@ -1,21 +1,25 @@
/**
- * PAPER WINDOW — the starter scenario, as data.
+ * BLACK PLATFORM — the starter scenario, as data.
*
- * Shipped as a Foundry ADVENTURE so the whole thing imports in one click: the GM
- * text, the seven player handouts as separate pages a GM can reveal one at a time,
- * the six pregens with their scenario functions and complications already written
- * into their biographies, the cast, the case file with its tracks and phases already
- * set, and the Borrowed Authority as a real item rather than a paragraph.
+ * Shipped as a Foundry ADVENTURE so the whole thing imports in one click: the GM text,
+ * the player handouts as separate pages a GM can reveal one at a time, the six pregens
+ * with their scenario jobs already in their biographies, the enemy roster, the two
+ * casualties, and the case file with its tracks and phases already set.
*
- * The handouts are their own journal with default ownership OBSERVER, because a
- * handout a GM has to copy out of the scenario text is a handout that does not get
- * used. The GM journal stays hidden.
+ * The handouts are their own journal at OBSERVER ownership, because a handout a GM has
+ * to copy out of the scenario text is a handout that does not get used. The GM journal
+ * stays hidden.
+ *
+ * This is an ACTION starter: descend, fight for the platform, board, rescue, fight
+ * along a moving train, stop it. One short clue scene and no social subsystem — which
+ * is also why it carries no contact group and no Borrowed Authority.
*/
const P = s => `${s}
`;
const H = (n, s) => `${s}`;
const UL = xs => `${xs.map(x => `- ${x}
`).join("")}
`;
const OL = xs => `${xs.map(x => `- ${x}
`).join("")}
`;
+const PRE = s => `${s}`;
const TABLE = (head, rows) =>
`${head.map(h => `| ${h} | `).join("")}
`
+ rows.map(r => `${r.map(c => `| ${c} | `).join("")}
`).join("")
@@ -29,726 +33,582 @@ const READ = s => `${s}
`;
export const STARTER_PAGES = [
{ name: "1 · Run this first", content:
- P("PAPER WINDOW is a six-player introductory case for The Custodians. "
- + "Four to five hours. Classification Near. Bureaucratic horror, "
- + "investigation, negotiation and one containable fight.")
- + P("It teaches the game while it is being played. Do not explain the "
- + "subsystems in the briefing. Introduce each one the first time the agents need it.")
+ P("BLACK PLATFORM is a six-player action starter for The Custodians. "
+ + "Three to four hours. Classification Near.")
+ + READ("A train is running through a station whose rails were taken out in 1976. Two "
+ + "engineers boarded it and have not come back. It returns every eleven minutes, and "
+ + "every time it comes back there are more people on it.")
+ + P("The case is a straight line: go down, take the platform, get "
+ + "aboard, find the engineers, fight forward along a moving train, and stop it before "
+ + "it reaches a terminus that does not exist in this world.")
+ + H(2, "What each scene teaches")
+ TABLE(["Scene", "Introduces"], [
- ["Briefing", "the agency, the case tracks, cover and objectives"],
- ["The cordon", "ordinary skill rolls and Borrowed Authority"],
- ["The dead-letter cage", "investigation, fail-forward clues and the case clock"],
- ["Window Six", "crossing and Coherence"],
- ["The night shift", "READ, SIGNAL, OFFER and Standing"],
- ["The Cancellation Man", "reaction lanes, cover, graded defences, armour, First Aid"],
- ["Closing the register", "specialist teamwork, Containment and a real choice"],
- ["Coming home", "extraction, return rolls, debrief and experience"]
+ ["Briefing", "case tracks, kit and objectives"],
+ ["Descent", "one skill check with a real hazard"],
+ ["Platform", "reaction lanes, cover, attacks, graded defences"],
+ ["Boarding", "movement and the action economy"],
+ ["Rescue carriage", "First Aid, wounds, a locked door"],
+ ["Moving train", "range, ammunition, shooting past bystanders"],
+ ["Driver's cab", "four jobs at once, against a clock"],
+ ["Return", "coming home, Coherence and experience"]
])
- + P("There is no clue that must be found and no roll that must succeed. "
- + "A failed roll changes the cost, the danger or the clarity of the information. It "
- + "never stops the scenario.")
- + H(2, "The six-player spotlight rule")
- + P("Every major scene names a spotlight agent. That player frames the "
- + "first approach. Others assist, supply dossier items, handle consequences or propose "
- + "alternatives. Do not solve another player's specialist scene with the Field "
- + "Lead's phase roll. The purpose of a six-player starter is six protagonists, "
- + "not six people present.")
+ + P("No failed roll hides the way forward. A failure makes the next "
+ + "thing harder or more expensive. It never removes the route.")
+ + H(2, "Keep it moving")
+ + P("Once the players understand a place, give them a sound — doors chiming, wheels "
+ + "speeding up, ticket punches getting closer — and ask what do you do now?")
+ + P("You do not have to kill everything. Inspectors withdraw, doors "
+ + "lock, the Conductor can be held off. The job is to stop the train.")
+ + P("The picture to aim at: six agents thrown forward as the brake takes, the hurt "
+ + "engineer strapped between the seats, the Conductor hammering on a raised riot "
+ + "shield, and the indicator clicking from FINAL TERMINUS to "
+ + "HOME.")
},
{ name: "2 · The briefing", content:
- READ("At 08:12 yesterday, six brown envelopes were delivered in Coventry. They bear "
- + "valid first-class postage, a 1987 postmark and the cancellation mark of Rookery Heath "
- + "Delivery Office, which closed in 1998.
"
- + "Each envelope was addressed to a living person. Within an hour of opening one, the "
- + "recipient disappeared from government databases. By evening, employers, banks and "
- + "close relatives had begun forgetting them. The people themselves remain physically "
- + "present.
"
- + "One recipient, Lorna Vale, went to the closed delivery office shortly after midnight. "
- + "She has not returned. At 03:17 the office's disconnected landline called the Cabinet "
- + "Office switchboard. A woman asked whether the Department would be making its last "
- + "collection.
"
- + "The building is scheduled for demolition at sixteen hundred today.")
+ READ("At 00:17 this morning, signalling equipment picked up a passenger train entering "
+ + "Black Platform, beneath Wych Street. The platform closed in 1976. The rails were "
+ + "lifted and both tunnels were filled with concrete.
"
+ + "The train stood there ninety seconds and left into the eastern tunnel. The cameras "
+ + "show twelve passengers. Three are dressed for 1950. One is dressed as a nurse. None "
+ + "of them moves while the train is stopped.
"
+ + "At 00:28 it came back.
"
+ + "Two Network Rail engineers went down at 01:06. At 01:12 one of them transmitted: "
+ + "“It has our names on the board.” Both boarded at 01:23. The train has gone "
+ + "round seven times since.
"
+ + "The next one arrives in eleven minutes.")
+ H(2, "Orders")
- + OL(["Enter Rookery Heath Delivery Office under the supplied cover.",
- "Recover Lorna Vale and the six original letters.",
- "Identify the source of the cancellation mark.",
- "Stop further deliveries.",
- "Leave no publicly intelligible incident."])
- + P("Cover: licensed asbestos and records-remediation team acting for "
- + "the demolition contractor.")
- + P("The brief omits: the agency attended the same building after a fire "
- + "in 1987. The file has been deliberately removed from the current case papers.")
+ + OL(["Get Leah Hall and Martin Cole back.",
+ "Board the train.",
+ "Find and remove whatever is holding the route open.",
+ "Stop the service before it reaches Final Terminus.",
+ "Come back with nothing left running under central London."])
+ + P("Cover: emergency electrical-response team. The station and the "
+ + "access tunnel are already shut to the public, so there is nobody to talk past.")
+ H(2, "Starting state")
- + P("Containment 8 · Resources 8 · Clearance 6 · Last Collection 0/4 · "
- + "all agents at Coherence 10. The case file in this adventure is already set to these.")
- + P("The six letters count as one secured exhibit once collected.")
+ + P("Containment 9 · Resources 8 · Clearance 5 · Terminus clock 0/6 · "
+ + "everyone at Coherence 10. The case file in this adventure is already set to these.")
+ + H(2, "Issued for this case")
+ + P("On top of normal kit: six sets of ear defenders, two locator tags, a recovery "
+ + "strop, insulated bolt-cutters, and a paper diagram of the closed platform.")
},
{ name: "3 · The truth", content:
- P("In 1987, Rookery Heath briefly overlapped a Near world in which the building was not "
- + "a postal depot but the Civil Continuity Registry. In that world a "
- + "person's civic existence depended on a deliverable name and address held in the "
- + "nightly register.")
- + P("The overlap happened during a fire. For eleven minutes both offices processed the "
- + "same mail. Three hundred and seventeen people acquired valid entries in "
- + "both worlds.")
- + P("Custodian Malcolm Rusk contained it by declaring Window Six, the "
- + "disputed register and the entire other office to be an undelivered agency "
- + "consignment, and sealing the declaration in the dead-letter cage. As long as "
- + "the building remained an unresolved delivery, the crossing stayed closed.")
- + P("Demolition workers broke the exterior seal three days ago. The Registry's night "
- + "shift resumed where it stopped in 1987. Its staff have spent thirty-nine years "
- + "waiting to be relieved, and their automatic system has begun delivering notices "
- + "that reconcile the disputed citizens by deleting one version from the record.")
- + P("There are two Lorna Vales. Both are forty-two, both have a complete "
- + "childhood, family and employment history. Neither is the original: the overlap "
- + "produced two equally valid continuations of the same infant recorded in 1987.")
- + P("The Registry staff are not invaders. They want a recognised authority to "
- + "take custody of the disputed folio so they can close their shift. The briefed "
- + "solution — destroying the other register — works, and erases 317 people who are as "
- + "real as anyone on the team.")
- + P("The clean solution is to accept the folio into agency custody as a "
- + "pending inter-jurisdictional register, using the original case number, a "
- + "working franking mechanism, the Registry's master stamp, a medical certification "
- + "that the duplicates are living persons, and a serving officer's signature. "
- + "It requires every member of the six-person team.")
+ P("The train is the 00:17 Wych Street evacuation service, from a Near "
+ + "world where London was abandoned after something went wrong in 1958. It went into a "
+ + "tunnel crossing and never arrived anywhere.")
+ + P("The passengers are echoes. They are not dangerous and they cannot "
+ + "be saved: take one off the train and it stops existing. The conductor and the "
+ + "inspectors are parts of the service rather than people. They enforce three rules:")
+ + OL(["Nobody travels without a valid destination.",
+ "Nobody gets off between stations.",
+ "The service reaches the terminus."])
+ + P("A brass route plate in the driver's cab is the physical anchor. It "
+ + "lists stations from both worlds, and it keeps quietly convincing reality that the "
+ + "route joins them up.")
+ + H(2, "The engineers")
+ + P("Both alive, in the rear service compartment. Leah Hall has a broken "
+ + "leg and is bleeding slowly. Martin Cole is concussed and on his "
+ + "feet. They worked out how to shut it down before they were attacked:")
+ + OL(["Open the route cabinet.",
+ "Reverse the field regulator.",
+ "Take out the brass route plate.",
+ "Pull the manual brake when the indicator says HOME."])
+ + P("The Conductor will try to stop every one of those.")
},
-{ name: "4 · Pressure — the Last Collection", content:
- P("Advance the clock when a phase fumbles; when the team argues twenty minutes without "
- + "acting; when a loud weapon is fired in the mundane building; when the Borrowed "
- + "Authority is burned; when the team violates the clerks' taboo after learning it; "
- + "or when passive investigation needs turning into a decision.")
- + P("Do not advance it merely because the players are investigating carefully.")
- + TABLE(["Clock", "Event"], [
- ["1 — Sorting",
- "The dead conveyor starts. Every letter in the building rotates so its address faces Window Six."],
- ["2 — Forwarded",
- "One affected recipient's spouse can no longer remember their face. Containment −1."],
- ["3 — At the gate",
- "The demolition manager arrives with workers and police support. The cover has about thirty minutes left. Clearance −1 if the team demands overt agency intervention."],
- ["4 — Last Collection",
- "Every disputed notice is delivered at once. Window Six locks open, the two offices begin occupying the same space, and the demolition crew witnesses it. Containment −3. The clean settlement is still possible; all remaining specialist tasks become Difficult."]
+{ name: "4 · Pressure — the Terminus clock", content:
+ P("Move it at the listed moments, on a fumbled phase, and whenever the group stands "
+ + "arguing while something dangerous is happening.")
+ + TABLE(["Clock", "What happens"], [
+ ["0 — Due", "The team is at the station. The train is eleven minutes out."],
+ ["1 — Arriving", "Headlights inside a tunnel filled with concrete. The live rail comes on."],
+ ["2 — Departed", "They are aboard. Mark the case Across. The platform outside is gone."],
+ ["3 — Between", "The windows show a dark, empty London. Everyone aboard witnesses a paradox: −1 Coherence, once."],
+ ["4 — Inspection", "Two ticket inspectors come forward from the rear."],
+ ["5 — Final approach", "It speeds up. Everything physical or fiddly is at −10%."],
+ ["6 — Terminus", "The white station fills the windows. One last round, then the emergency ending."]
])
- + P("If the clock would pass 4, reduce Clearance or Containment instead, according to "
- + "whether the cause was disobedience or public exposure.")
+ + P("Normally it moves once after each big scene — and once every round during "
+ + "the cab sequence.")
},
-{ name: "5 · Phase one — the demolition cordon", content:
- P("A low concrete building behind temporary fencing. Half the red sign has been removed, "
- + "leaving DELIVE OFF over the entrance. Rainwater lies everywhere "
- + "except on the front step.")
- + P("Gareth Moss, site manager, is competent, worried about his schedule "
- + "and eager to transfer responsibility. He has opened no letter. He accepts Imogen "
- + "Price's credential if the team behaves like a survey crew.")
- + H(2, "Teach: Borrowed Authority")
- + P("Ask the group how they use the identity without breaking its red flags. Signing in "
- + "satisfies the first. A six-person team satisfies the second. Photographing each "
- + "opened door satisfies the third and creates useful evidence.")
- + P("Routine entry costs 1 Trust and needs no roll. Demanding that demolition stop is a "
- + "stretch, or Clearance spent on overt agency authority.")
- + H(2, "Field Lead spotlight — choose the posture")
- + UL(["Quiet: demolition continues outside. +20% Stealth inside; Last "
- + "Collection advances if the crew reaches Window Six.",
- "Official: spend 1 Clearance to suspend demolition two hours. No "
- + "public clock pressure, but the agency knows the team went beyond the brief.",
- "Borrowed: stretch Price's scope for a temporary asbestos exclusion. "
- + "Success buys two hours free; failure buys thirty minutes; a fumble burns the credential."])
- + H(2, "What Gareth gives without a roll")
- + P("If treated as a professional rather than a witness to be managed:")
- + UL(["The cage door was found open three days ago, although its lock was rusted shut.",
- "Lorna Vale entered using a staff key bearing the old crown logo.",
- "A landline rings inside every day at 15:42.",
- "The demolition plan contains no Window Six."])
+{ name: "5 · Phase one — the descent", content:
+ P("In through a maintenance door and down 140 steps. The old posters are still tiled to "
+ + "the walls, and every one of them has been changed to advertise the 00:17 service.")
+ + P("At the bottom, the traction rail that was taken out in 1976 is back, and it is live. "
+ + "The access gate has dropped across the stairwell. Past it, the platform lights are "
+ + "coming on one at a time.")
+ + H(2, "The obstacle")
+ + P("Ten metres of wet floor beside a live rail, and a steel gate to get up, before the "
+ + "train arrives. Pick one lead:")
+ + UL(["Engineer — Repair (Electronics), to kill the rail.",
+ "Criminal — Sleight of Hand, to open the gate control without stepping in the water.",
+ "Containment — Athletics, to hold the gate up while everyone goes under.",
+ "Lead — Tradecraft, to run the whole thing: insulation, line, order of movement."])
+ + TABLE(["Result", "What happens"], [
+ ["Success", "Everyone crosses."],
+ ["Special or critical", "Everyone crosses and picks good ground: +10% on the first reaction or attack roll."],
+ ["Failure", "Everyone crosses. One agent, chosen at random, takes 1d3 damage, ignoring armour."],
+ ["Fumble", "As failure, the lights go out, and Terminus advances."]
+ ])
+ + P("The doctor can treat that immediately. It is a small, safe introduction to damage "
+ + "and healing before anybody is shooting.")
+ + P("Advance Terminus to 1 — Arriving.")
},
-{ name: "6 · Phase two — the dead-letter cage", content:
- P("Sorting frames, sacks hardened by damp, a secure wire cage. Electricity disconnected. "
- + "The fluorescent lights come on when nobody is looking directly at them.")
- + P("Give the group the plan and ask where everyone goes. Keep all six active by cutting "
- + "between simultaneous searches.")
- + P("Every discovery is automatic when the agents search the right place. "
- + "A success adds interpretation or advantage; a failure gives the clue with a cost; a "
- + "fumble also advances Last Collection.")
- + H(2, "A · The 1987 agency carbon — Registry or Research")
- + P("In the manager's desk, a carbon headed R-143 / UNDELIVERABLE MATERIAL.")
- + UL(["Success: “pending collection” is the containment, not "
- + "ordinary wording. Record case number R-143 for the clean ending.",
- "Failure: found and read, but removing it tears the line with the "
- + "signing officer's name. Resources −1 to authenticate later.",
- "Special/critical: the page numbers jump from 14 to 22. The agency "
- + "knew, and removed the intervening report."])
- + H(2, "B · The six letters — Doctor")
- + P("Five wait in numbered pigeonholes; the sixth is opened and addressed to Lorna Vale. "
- + "The ink contains human proteins. The paper fibres carry the addressee's own "
- + "mitochondrial markers.")
- + UL(["Success: these are not messages about the recipients, they "
- + "are partial physical records of them. Destroying one may injure or erase "
- + "its addressee. +20% when certifying the disputed people later.",
- "Failure: same conclusion, after a sample makes one recipient briefly "
- + "lose their surname. Witnessed paradox: −1 Coherence to the doctor.",
- "Critical: the doctor's own future death notice is medically genuine "
- + "and has not yet been written."])
- + H(2, "C · Window Six — Engineer")
- + P("Behind a sorting frame, a service window absent from the building plan. Roller shutter "
- + "painted shut. The brass franking machine beside it has no power connection and is warm.")
- + UL(["Success: the window is a regulator whose state is expressed through "
- + "postal procedure, and it is repairable. Record franker diagnosed: "
- + "+20% on the later repair.",
- "Failure: learns how it works by opening the shutter two centimetres. "
- + "Everyone hears rain falling upward. Containment −1.",
- "Fumble: as failure, and the machine stamps the engineer's hand with "
- + "tomorrow's date. Witnessed paradox."])
- + H(2, "D · The master key and the impossible fingerprints — Criminal")
- + P("Two mechanisms: an ordinary warded lock, and a bolt that moves only while the landline "
- + "is ringing. Sleight of Hand opens the first. The second yields to calling the "
- + "disconnected number from the agency handset, waiting for 15:42, or Gareth's staff key.")
- + UL(["Success: in without marking the lock, and the old crown staff key.",
- "Failure: in, with visible damage — 1 Trust or 1 Clearance to suppress it.",
- "Fumble: the cage closes with the criminal inside as the landline rings. "
- + "They may answer. A woman says, “Collection is overdue, Mr Roake.” "
- + "Advance Last Collection."])
- + P("Inside the cage are the criminal's fingerprints under the identity Arthur "
- + "Roake, postal porter, deceased 19 September 1987.")
- + H(2, "Opening Window Six")
- + P("The disconnected telephone rings once. A woman asks: “Collection or delivery?”")
- + P("“Collection” opens the shutter safely. “Delivery” "
- + "opens it violently and advances Last Collection. Any careful reading of R-143 shows the "
- + "consignment is pending collection.")
- + P("Mark the case Across. Going out is a decision, not a roll — the "
- + "Transposition roll happens coming home. The mundane shutter closes behind the last "
- + "agent and can still be opened from either side.")
-},
-
-{ name: "7 · Phase three — the night shift", content:
- P("The Near office is clean, warm and fully staffed. Six clerks in 1987 uniforms work "
- + "beneath green lamps. None has aged. Pneumatic tubes carry sealed envelopes into a "
- + "ceiling with no space above it.")
- + P("At Window Six stands Evelyn Vale, night supervisor. Behind her is "
- + "Lorna Vale, the missing recipient — and another Lorna Vale "
- + "in Registry uniform. They are physically identical.")
- + P("Seeing the two Lornas is a witnessed paradox. It is not meeting "
- + "yourself unless an agent meets their own duplicate.")
- + H(2, "READ")
- + P("Insight, Anthropology or relevant professional observation. Facts in order:")
- + OL(["Taboo: never open, read aloud or name the addressee of mail that is "
- + "not yours. A sealed identity belongs to its recipient.",
- "Desire: they want a recognised authority to relieve them and receipt "
- + "the disputed folio. Thirty-nine years on the same shift.",
- "Critical extra: they are frightened of the Cancellation Man, which is "
- + "not a person but the procedure that destroys records that cannot be reconciled."])
- + H(2, "SIGNAL")
- + P("They speak English; the difficulty is procedural. A correct signal gives name, "
- + "department and case reference from the public side of the counter: "
- + "“Amira Bell, Departmental Custody, attending R-143 for collection.”")
- + UL(["With R-143 — Easy.",
- "Without it — Average.",
- "From behind the counter — Difficult, and it violates their working boundary."])
- + H(2, "OFFER")
- + P("A signed receipt and a promise to take the folio into recognised custody, passed "
- + "through Window Six in exchange for it.")
- + UL(["Correct form — Easy Bargain.",
- "A promise to “look into it” — Average.",
- "Demanding surrender or threatening destruction — Difficult."])
- + P("At +2 or better Evelyn explains the clean closure and releases the "
- + "folio once the six requirements are met. At +1 she explains only that "
- + "the register needs custody and certification. At 0 she permits "
- + "inspection but will not release it. At negative, she activates the "
- + "Cancellation Man.")
- + H(2, "The two Lornas")
- + P("Same childhood until the 1987 fire, different lives after. The mundane Lorna has a "
- + "partner and works for the council. Registry Lorna has worked this shift for "
- + "thirty-nine subjective years and remembers a daughter born in the Near world.")
- + P("Neither wants the other erased. Asked which is real, each answers: "
- + "“Both, until you file otherwise.”")
- + P("Any successful Medicine or Biology roll establishes that both are living humans and "
- + "neither is a constructed copy. Failure leaves it medically apparent but costs the "
- + "later certification its +20%.")
-},
-
-{ name: "8 · The Cancellation Man", content:
- P("Trigger when the team tries to remove or destroy the folio without an "
- + "agreement; when someone knowingly violates the taboo; when Standing reaches −1; or when "
- + "the table needs the promised physical tutorial before the final act.")
- + P("The lights extinguish from the back of the room toward the team. A tall figure unfolds "
- + "from returned envelopes, red twine and black cancellation ink. Every address "
- + "on it belongs to someone currently in the room.")
- + P("It wants the disputed folio, not casualties. It moves toward the "
- + "carrier and ignores other agents unless they obstruct it.")
- + H(2, "In Foundry")
- + P("Use the Cancellation Man actor in this adventure, or any Dangerous "
- + "adversary. It withdraws at 2 hit points, when restrained, or when presented with a "
- + "properly bagged and receipted folio. For a gentler first session, drop its attack by "
- + "10% and start it a full move away.")
- + H(2, "Terrain")
- + UL(["Sorting frames — solid partial cover.",
- "The service counter — blocks movement, not attacks.",
- "Closing a wire cage around it — one action, and Sleight of Hand, Repair (Mechanical) or Athletics.",
- "The Containment Officer can interpose a raised riot shield between it and the carrier."])
- + H(2, "Teach combat in this order")
+{ name: "6 · Phase two — take the platform", content:
+ P("Headlights come through solid concrete. The train arrives in complete silence and only "
+ + "becomes audible when the doors open.")
+ + P("Four ticket inspectors step out. Dark blue uniforms, blank faces under peaked caps, "
+ + "each with a wooden ticket punch held like a short baton.")
+ + READ("No destination shown.")
+ + P("Then they come forward.")
+ + H(2, "The platform")
+ + PRE("WEST TUNNEL 40 m PLATFORM EAST TUNNEL\n"
+ + "=================[ rear ][ centre ][ front/cab ]=================\n"
+ + " stairs/gate pillars every 8 m signal box\n"
+ + " team starts here partial cover train doors")
+ + UL(["Tiled pillars are solid partial cover.",
+ "The train is 15 m from the stair gate.",
+ "The platform edge is a two-metre drop onto the live track.",
+ "Shoved over the edge? Athletics or Dodge to catch the lip. Failure is 1d6 and prone."])
+ + H(2, "The inspectors")
+ + P("Four of them. The statblock is in this adventure.")
+ + UL(["They hit with ticket punches — treat as a cudgel.",
+ "They go for the nearest agent still standing, and ignore anyone already down.",
+ "They dodge. They have no firearms.",
+ "When three are down, the last one withdraws."])
+ + H(2, "Teach the fight in this order")
+ OL(["Roll reaction lanes.",
- "Ask each player for movement and one significant action.",
- "Explain range, cover or shield modifiers only when they apply.",
- "When attacked, demonstrate Dodge or Parry and the escalating defence penalty.",
- "If a wound penetrates, resolve location, armour and any Major Wound.",
- "Give the doctor an immediate First Aid decision if anyone bleeds or goes down."])
- + H(2, "Nonviolent containment — always available")
- + UL(["Bag the folio and attach a provisional receipt: Bargain or Status, Average. Chain of "
- + "Custody is exactly this expertise.",
- "Lock it in the dead-letter cage: the criminal works the lock, Containment holds it off.",
- "Repair the franking machine and stamp the receipt early: the engineer acts, another agent covers."])
- + P("Killing it is temporary. It reforms at the next clock advance. "
- + "Containing it teaches the intended lesson: violence buys time, procedure closes the case.")
+ "Show Callum how to raise the riot shield.",
+ "Point out the pillars before anyone attacks.",
+ "Let each hit stand as a blow, so the defender can answer it.",
+ "Resolve armour, hit location and wounds normally.",
+ "Remind them that moving does not use up their attack."])
+ + H(2, "Getting aboard")
+ + P("When three inspectors are down the doors start chiming. Two rounds to "
+ + "board.")
+ + UL(["An agent on their feet just walks on.",
+ "Carrying somebody down needs a second person or the recovery strop.",
+ "Still outside when the doors close? Force them with Athletics, the entry bar or the bolt-cutters.",
+ "Nobody gets left behind. A failed attempt still gets them aboard, and costs Resources −1 in dropped kit."])
+ + P("Advance Terminus to 2 — Departed, and mark the case Across.")
},
-{ name: "9 · Phase four — closing the register", content:
- P("Put six blank lines on the table, one per contribution. Tell the players the case can "
- + "now be closed. Do not prescribe which ending.")
- + H(2, "The clean settlement")
- + P("Enter the folio into agency custody as R-143-A, Pending Inter-Jurisdictional "
- + "Register. This decides nothing about which world owns the 317 names. It "
- + "establishes a third recognised custodian and lets the Registry close its shift without "
- + "deleting anyone.")
- + TABLE(["#", "Requirement", "Who", "Roll", "On failure"], [
- ["1", "Establish authority", "Field Lead", "Spend 1 Clearance, or Status/Tradecraft to justify it",
- "The Clearance is spent anyway. Fumble also marks the lead for internal investigation."],
- ["2", "Hold containment", "Containment", "Athletics, Shield or a practical plan",
- "Containment −1; the work continues. A critical prevents the next clock advance."],
- ["3", "Write the settlement", "Settlements", "Bargain, Persuade or Knowledge — Easy at impression +2",
- "A future obligation: the Registry staff are technically seconded, and may later demand pay, pension and extraction."],
- ["4", "Certify the living", "Doctor", "Medicine, +20% if the letters and both Lornas were examined",
- "Still certified, but the doctor is personally responsible for reviewing all 317 cases."],
- ["5", "Repair and frank", "Engineer", "Repair (Electronics) or Engineering, +20% if diagnosed",
- "Resources −1. Fumble works but dates the receipt one year in the future — a campaign hook."],
- ["6", "Recover the master stamp", "Criminal", "Sleight of Hand, from the public side of the counter",
- "Obtained, but the drawer breaks: impression −1. Fumble opens a second drawer holding an undelivered letter addressed to the criminal."]
+{ name: "7 · Phase three — seize the train", content:
+ PRE("[ REAR SERVICE ] -- [ PASSENGER CARRIAGE ] -- [ FRONT / DRIVER'S CAB ]\n"
+ + " trapped engineers echo passengers route plate\n"
+ + " locked door inspectors enter here conductor")
+ + P("They can stay together, but splitting is faster:")
+ + UL(["Rescue group — doctor, criminal and one bodyguard, going back.",
+ "Assault group — lead, containment, overwatch and/or engineer, going forward."])
+ + P("Cut between the two groups every action or two. Nobody should sit "
+ + "out a whole scene.")
+ + H(2, "The rear service compartment")
+ + P("The door is locked and has no handle on this side.")
+ + UL(["Sleight of Hand opens it.",
+ "An entry bar and Athletics force it.",
+ "Shooting the lock opens it and advances Terminus immediately."])
+ + P("A failure still opens the door. It just takes long enough that "
+ + "Inspection arrives early.")
+ + H(3, "Leah Hall")
+ + P("Pinned under a folded maintenance seat. Left leg disabled, 4 hit "
+ + "points, bleeding slowly.")
+ + UL(["Lift the seat: Athletics, or an entry bar.",
+ "Stop the bleeding: First Aid.",
+ "Move her safely: the recovery strop, or two people."])
+ + P("First Aid works normally: success stops the bleeding and gives hit points back; "
+ + "failure does neither, and that wound has had its one attempt. She "
+ + "can still be carried while somebody keeps pressure on, but she goes on bleeding at "
+ + "the end of each round until she is treated another way or the case ends.")
+ + H(3, "Martin Cole")
+ + P("Concussed, 7 hit points, awake and frightened. He knows the four "
+ + "steps. Calm him or treat him and he says it clearly; otherwise he blurts it in the "
+ + "wrong order and they still get all four.")
+ + H(2, "The passenger carriage")
+ + P("Twelve echoes sitting still. Their tickets are for places demolished decades ago.")
+ + P("Walking through is safe until Terminus 4. Searching turns up a route diagram showing "
+ + "HOME between Wych Street and Final Terminus, a first-aid box worth "
+ + "one use of dressings, and two locked connecting doors — the criminal opens those "
+ + "quietly, and forcing one gives the Conductor warning.")
+ + P("At Terminus 3 — Between, the windows show an empty London under a "
+ + "white sky. Witnessed paradox, once, for everyone aboard.")
+ + H(2, "The inspection fight")
+ + P("At Terminus 4, three inspectors come in from the rear. If the one from the platform "
+ + "got away, it comes with them.")
+ + UL(["Seats are partial cover.",
+ "Anything further off than one carriage is medium range.",
+ "Shooting down a carriage with echoes in it is −20%, and on a fumble the shot destroys an echo: Containment −1.",
+ "They try to grab Leah or Martin and mark them RETURNED.",
+ "An inspector withdraws at 2 hit points, or when restrained."])
+ + P("The carriage is why everyone has a melee weapon. A rifle owns the "
+ + "long aisle, but an inspector that reaches the rescue group is close enough that a "
+ + "knife, a cudgel, an entry bar, a shield or bare hands is the cleaner answer. Let the "
+ + "players pick — do not tell them which one is correct.")
+ + P("Move to the cab as soon as the engineers can walk and somebody reaches the forward "
+ + "door. Do not make them clear the carriage first.")
+},
+
+{ name: "8 · Phase four — stop the service", content:
+ P("The cab is bigger inside than the front of the train. Brass levers cover one wall. The "
+ + "speed indicator has station names on it instead of numbers.")
+ + P("The Conductor is at the controls: tall, black uniform, a polished "
+ + "face with nothing on it. Its gloved hand has fused around the throttle.")
+ + READ("This service terminates. All passengers remain aboard.")
+ + P("Then it attacks.")
+ + H(2, "The Conductor")
+ + UL(["It goes for whoever is working a control.",
+ "In order: the route plate first, then the regulator, then the brake.",
+ "It does not chase anyone backing off toward the rear.",
+ "It can be restrained. It cannot be talked to.",
+ "At 2 hit points it is driven back, not killed, and keeps trying to reach the controls at half speed."])
+ + H(2, "The final sequence")
+ + P("Start Terminus at 3 if they got here fast, or wherever it already is. "
+ + "At the end of every round, add 1.")
+ + P("Four jobs, and they can all happen at once.")
+ + TABLE(["Job", "Who", "Roll", "What happens"], [
+ ["A · Open the route cabinet", "Criminal", "Sleight of Hand, Average",
+ "Success opens it now. Failure opens it at the end of the next round. Fumble opens it, and an arc does 1d3 and knocks something out of their hands."],
+ ["B · Reverse the regulator", "Engineer", "Engineering or Repair, Difficult",
+ "Needs 3 progress. Success 1, special 2, critical 3. Failure still gives 1 and does 1d3 — it is being worked out by trial and error. Fumble loses 1 and does 1d3."],
+ ["C · Free the manual brake", "Containment", "Athletics, entry bar or bolt-cutters, Average",
+ "Success frees it. Failure frees it at the end of the next round. Fumble frees it and leaves them flat under the lever."],
+ ["D · Call HOME", "Field Lead", "Tradecraft, Navigate or Spot, Average",
+ "Success catches the right moment. Special or critical gives the brake +20%. Failure still catches it, but so late that the brake roll is Difficult."]
])
- + P("When all six are on the form, the Field Lead reads the case number aloud and Evelyn "
- + "stamps the other copy. Every clock in the Near office moves from 15:42 to "
- + "15:43.")
- + P("The clerks put on coats. Some cry. None of them knows what weather waits outside. The "
- + "Near Lorna asks the mundane Lorna whether she would like to meet her daughter. It is "
- + "not a trick and does not have to be resolved tonight.")
- + P("Result: Containment +1; Clearance −1 if the team openly defied orders; "
- + "Standing with the Returned Clerks +1. All six affected recipients begin returning to "
- + "records over the next day.")
- + H(2, "The other endings")
- + UL(["Destroy the Near register. The agency's implied preference. The six "
- + "mundane recipients are restored; the Registry staff, Near Lorna and 311 others become "
- + "unrecorded and then physically absent. Watching it is a witnessed paradox; doing it "
- + "knowingly is also broke local law. Containment +2, Clearance +1. At debrief nobody "
- + "uses the word “people” unless a player does first.",
- "Let the Registry supersede our record. The mundane six are progressively "
- + "forgotten and their Near counterparts take their addresses, jobs and relationships. The "
- + "team recovers Lorna — the other Lorna. Containment unchanged, Clearance −2. Grave, and "
- + "not a game over.",
- "Seal it again. Recreate Rusk's 1987 containment. Containment +1; the case "
- + "is contained, not closed; the clock resets to 1 and starts again when the "
- + "building is next disturbed. Somebody must stop the demolition permanently.",
- "Fail to choose. At Last Collection 4 the buildings overlap and the crew "
- + "sees the Registry. The team can still escape. Close on sirens arriving at a building "
- + "that officially employs everyone standing outside it."])
+ + P("Martin's instructions give the engineer +20%.")
+ + P("Everyone else: overwatch takes the Conductor or the inspectors "
+ + "coming forward; the doctor keeps Leah alive and patches whoever is bleeding; anyone "
+ + "spare can assist, guard a specialist, or hold a door shut.")
+ + H(2, "Pulling the brake")
+ + P("Once the cabinet is open, the regulator has 3 progress and the lead has called HOME: "
+ + "take the plate out — an action, no roll — and pull the brake on Athletics.")
+ + TABLE(["Result", "What happens"], [
+ ["Success or better", "It stops at Black Platform. The crossing is steady for the way home."],
+ ["Failure", "The back half stops at Black Platform and the front half is still Across. Everyone gets home; one piece of carried kit is lost per failed return roll."],
+ ["Fumble", "The lever snaps. Go to the emergency ending."]
+ ])
+ + H(2, "Emergency ending — Terminus 6")
+ + P("Describe the enormous white station filling the windows, and give them one more "
+ + "round. If the train is not stopped by the end of it — or the lever broke — there is "
+ + "still a way out:")
+ + OL(["Smash or shoot the exposed route plate. One action, no roll.",
+ "The crossing tears open for one round.",
+ "Everybody makes a normal return roll. Another agent can aim it for them."])
+ + P("The engineers get home if somebody has hold of them. The train and the echoes are "
+ + "destroyed between worlds.")
+ + P("Containment −2. Everyone who watches it pays witnessed paradox. "
+ + "Return rolls are Difficult, because the window is closing as they go through.")
+ + P("This is an expensive win, not a wipe.")
},
-{ name: "10 · Extraction and debrief", content:
- P("Use the normal return procedure in every ending. A clean settlement leaves the "
- + "window clock at zero and adds no case-specific penalty.")
- + UL(["Another agent may aim the return for someone else — the team's best Transposition.",
- "Failure gets an agent home near the target; it never strands them.",
- "A fumble costs Coherence and may grant a Quiet Art.",
- "PAPER WINDOW is Near: no classification penalty."])
- + P("This team has no Anchor Officer, and that is deliberate. "
- + "Transposition is trained by exactly one posting in the register, and none of "
- + "these six has it: every one of them aims the return at the printed 1%. "
- + "Do not roll six dice to discover that. Narrate it: they all get home, "
- + "and none of them gets home neatly — wrong street, wrong hour, one of them in the "
- + "wrong coat. Roll only if an agent has a reason to want a clean arrival, or if the "
- + "window clock has moved and you want the fumble on the table.")
- + P("It is worth saying out loud at the debrief that the department sent six people to "
- + "a crossing and no anchor. That is the campaign, not an oversight.")
- + P("When everyone is home, close the case and resolve experience.")
- + H(2, "Debrief — ask each player one")
- + OL(["Field Lead: at what moment did this stop being the case in your orders?",
- "Containment: what did you protect that the department would rather was destroyed?",
- "Settlements: which sentence in the agreement do you expect to regret?",
- "Doctor: are the two Lornas the same person? Answer medically, then answer honestly.",
- "Engineer: did you repair Window Six, or teach it a new procedure?",
- "Criminal: what was written on the letter you left sealed?"])
- + READ("Whose name did you knowingly leave off the form?")
- + P("On a clean settlement each agent recovers the normal debrief Coherence. If the team "
- + "destroyed or superseded a register, ask the question before recovery.")
+{ name: "9 · Endings", content:
+ H(2, "Clean stop")
+ + P("It brakes into Black Platform. The echoes stay in their seats while daylight comes "
+ + "down the stairwell for the first time in fifty years. The Conductor and the "
+ + "inspectors become empty uniforms.")
+ + P("When the brass plate crosses onto the platform, the train goes. The rails cool and "
+ + "end at concrete again.")
+ + UL(["Both engineers out.", "Containment +1.",
+ "The route plate recovered as anomalous evidence.",
+ "Normal return rolls, and no extra penalty."])
+ + H(2, "Rough stop")
+ + P("Half home. They get the engineers and the plate; some kit — and possibly some "
+ + "evidence — stays Across.")
+ + UL(["Containment unchanged.", "Normal return rolls; failures lose carried kit.",
+ "Whether the plate comes back depends on who was holding it."])
+ + H(2, "Emergency closure")
+ + P("The train is destroyed at Final Terminus and they go through a collapsing crossing.")
+ + UL(["Both engineers out, if somebody had hold of them.", "Containment −2.",
+ "Difficult return rolls.", "A fumbled return may grant a Quiet Art as normal."])
+ + H(2, "Walk away")
+ + P("If they leave without the engineers or the plate, they can still get home. The "
+ + "service is back under Wych Street eleven minutes later with more people on it, and "
+ + "the Terminus clock resets to 2.")
+ + P("The case stays open. This is the only ending that does not stop it.")
+},
+
+{ name: "10 · Coming home and debrief", content:
+ P("Normal return procedure for everyone who was Across. Another agent can aim the return "
+ + "for somebody else. Nothing strands anyone.")
+ + P("None of these six is an Anchor Officer — only that posting trains "
+ + "Transposition — so they are all aiming at the printed base. Do not roll six dice to "
+ + "discover that. Say they all get home and none of them gets home neatly: wrong "
+ + "street, wrong hour, one of them still holding a ticket punch. Roll it properly only "
+ + "if somebody needs a clean arrival, or if you want a fumble on the table.")
+ + P("Then settle the case tracks, close the case, improve ticked skills, and give back "
+ + "the usual debrief Coherence.")
+ + H(2, "Debrief — one each, quickly")
+ + OL(["Lead: who did you send into it first?",
+ "Containment: what hit the shield hard enough that you still feel it?",
+ "Overwatch: which shot did you decide not to take?",
+ "Engineer: was that a machine, or was it following a rule?",
+ "Doctor: which of them would have died without you?",
+ "Criminal: what else was in the route cabinet?"])
+ + READ("When the doors opened, why did you get on?")
},
{ name: "11 · GM quick reference", content:
- H(2, "Cast")
- + TABLE(["Who", "Wants", "Lies about"], [
- ["Gareth Moss, site manager",
- "a signed instruction and someone else liable for the delay",
- "claiming he never looked through Window Six"],
- ["Lorna Vale, mundane",
- "her life and the truth, both", "having no curiosity about the other Lorna's daughter"],
- ["Lorna Vale, Registry clerk",
- "the shift to end without taking her existence with it",
- "never having sent a letter to the mundane world before"],
- ["Evelyn Vale, night supervisor",
- "a valid receipt, more than rescue",
- "nothing — but she answers only the question procedurally put to her"],
- ["The Cancellation Man",
- "unresolved records deleted or returned",
- "it cannot bargain, but it obeys valid packaging and receipts"]
+ H(2, "Running order")
+ + OL(["The gate and the live rail.",
+ "Four inspectors on the platform.",
+ "Board inside two rounds.",
+ "Split: rescue at the back, assault at the front.",
+ "Get Leah stable, get the four steps out of Martin.",
+ "Three or four inspectors in the carriage.",
+ "The Conductor, while four jobs happen at once.",
+ "Pull the brake. Go home."])
+ + H(2, "Enemies")
+ + TABLE(["Enemy", "Behaviour", "Withdraws when"], [
+ ["Platform inspectors ×4", "Nearest agent still standing. Ticket punches.", "three of them are down"],
+ ["Carriage inspectors ×3–4", "Go for the engineers. Use the seats.", "at 2 hit points, or restrained"],
+ ["The Conductor ×1", "Attacks whoever is working a control.", "driven back at 2, then half speed"]
])
- + H(2, "Clue map")
- + TABLE(["Conclusion", "Evidence routes"], [
- ["The agency knew", "R-143 carbon; missing page numbers; the lead's impossible cover employment"],
- ["Window Six is the crossing", "engineer's diagnosis; absent building plan; upward rain"],
- ["Letters rewrite people", "doctor's analysis; recipients forgotten; Registry explanation"],
- ["The clerks are trapped, not invading", "READ; frozen clocks; relief rota; Evelyn's offer"],
- ["Both Lornas are real", "medical exam; separate post-1987 memories; the disputed folio"],
- ["Clean closure is possible", "R-143 wording; contact at +1/+2; repaired franking mechanism"]
- ])
- + H(2, "Fail-forward costs")
- + P("When uncertain, charge one of these rather than withholding information — and never "
- + "more than one major cost for a single failed roll unless it is a fumble.")
- + UL(["Resources −1", "Containment −1", "Clearance −1", "Advance Last Collection",
- "Apply a listed Coherence occurrence", "Reduce impression by 1",
- "Attach a future obligation to the successful outcome"])
- + H(2, "Time management")
- + TABLE(["Real time", "Target position"], [
- ["0:00–0:20", "Briefing and character introductions"],
- ["0:20–1:00", "Cordon and Borrowed Authority"],
- ["1:00–2:00", "Investigation and Window Six"],
- ["2:00–3:00", "Contact and the two Lornas"],
- ["3:00–3:40", "The Cancellation Man"],
- ["3:40–4:30", "Closure, extraction and debrief"]
- ])
- + P("Short on time: let the cage open automatically when the telephone rings, and run the "
- + "Cancellation Man as a three-round containment challenge rather than combat to zero.")
+ + H(2, "Turning it up or down")
+ + P("Gentler: three inspectors on the platform and two in the carriage; "
+ + "enemy attacks at −10%; start the final sequence at Terminus 2; let Martin's "
+ + "instructions make the regulator Average instead of giving +20%.")
+ + P("Harder: add one armed figure with a revolver in the passenger "
+ + "carriage; start the final sequence at Terminus 4; let the Conductor call one "
+ + "defeated inspector back into service, once.")
+ H(2, "Spotlight checklist")
- + UL(["Lead chose the posture and signed the outcome.",
- "Containment raised the shield, protected someone or secured the folio.",
- "Settlements completed READ/SIGNAL/OFFER or drafted the receipt.",
- "Doctor examined the letters or the Lornas, and had a First Aid opportunity.",
- "Engineer diagnosed and repaired Window Six.",
- "Criminal entered the cage and recovered the master stamp."])
- + P("If a box is still empty approaching the final scene, move that contribution into that "
- + "player's path.")
-},
-
-{ name: "12 · Continuing the campaign", content:
- P("On the clean ending the agency holds the R-143-A Disputed Folio. It "
- + "generates cases without imposing a metaplot:")
- + UL(["A name appears in the folio before the team meets its owner.",
- "The future date from a fumbled repair arrives.",
- "The Returned Clerks submit thirty-nine years of back-pay claims.",
- "Near Lorna asks the team to extract her daughter.",
- "A senior officer orders the folio destroyed, and supplies perfect paperwork.",
- "Malcolm Rusk signs a new collection receipt, having died in 1994."])
- + P("Precedent: R-143-A recognises duplicate civic identities as living "
- + "persons pending adjudication. Trust 3 on a clean settlement, 1 if "
- + "merely resealed. Red flag: every invocation adds another name to the "
- + "folio. Unresolved: the agency accepted custody and never promised a "
- + "decision date.")
- + READ("A firearm can stop the thing coming down the corridor, but only a correctly worded "
- + "record can make the corridor stop existing.")
+ + UL(["Lead picked the approach and called HOME.",
+ "Containment used the shield and freed the brake.",
+ "Overwatch held a long lane, on the platform or down the aisle.",
+ "Engineer reversed the regulator.",
+ "Doctor stabilised Leah or patched somebody under fire.",
+ "Criminal opened the service door and the route cabinet."])
+ + P("If a box is still empty going into the cab, move that job into that player's path.")
}
];
/* ---------------------------------------------------------------- */
-/* Player handouts — their own journal, visible to players */
+/* Player handouts */
/* ---------------------------------------------------------------- */
export const STARTER_HANDOUTS = [
{ name: "1 · Operational brief", content:
P("DEPARTMENTAL CUSTODY SERVICE
"
- + "CASE: PW-01 / ROOKERY HEATH
"
- + "CLASSIFICATION: NEAR / LOCAL AUTHORITY EXPOSURE
"
- + "REPORT: 08:30, Gate 2, Rookery Heath Delivery Office, Coventry")
- + H(2, "Situation")
- + P("Six letters bearing valid 1987 postmarks were delivered yesterday from an office "
- + "closed in 1998. Opening a letter causes the addressee to disappear progressively "
- + "from public and private records.")
- + P("One recipient, Lorna Vale, entered the delivery office at 00:41 and "
- + "has not returned.")
- + P("The disconnected office landline called the Cabinet Office at 03:17. The caller asked "
- + "whether the Department would be making its last collection.")
- + P("Demolition is scheduled for 16:00.")
+ + "CASE: BP-017 / WYCH STREET
"
+ + "CLASSIFICATION: NEAR
"
+ + "REPORT TO: closed maintenance entrance, Wych Street, London")
+ + P("At 00:17, signalling equipment picked up a passenger train entering a platform "
+ + "closed since 1976. The rails were lifted and both tunnels filled with concrete.")
+ + P("The train comes back every eleven minutes.")
+ + P("Two Network Rail engineers boarded it. They have not come back.")
+ H(2, "Orders")
- + OL(["Enter under asbestos and records-remediation cover.",
- "Recover Lorna Vale and the six original letters.",
- "Identify the cancellation source.",
- "Stop further deliveries.",
- "Leave no publicly intelligible incident."])
- + H(2, "Starting state")
- + P("Containment 8 · Resources 8 · Clearance 6")
+ + OL(["Get Leah Hall and Martin Cole back.",
+ "Board the train.",
+ "Find and remove whatever is holding its route open.",
+ "Stop it before Final Terminus.",
+ "Come back with nothing left running."])
+ + P("Containment 9 · Resources 8 · Clearance 5")
},
-{ name: "2 · Borrowed Authority — Imogen Price", content:
- P("IMOGEN PRICE
Senior Asbestos Surveyor
"
- + "Halcyon Environmental Compliance Ltd")
- + P("Scope: routine site inspection
Trust: 2")
- + H(2, "Observed working habits")
- + UL(["Signs in at every controlled site.",
- "Never conducts an inspection alone.",
- "Photographs every opened door."])
- + P("Inside scope the credential works. One tier beyond scope requires a stretch. "
- + "A fumble burns it.")
-},
-
-{ name: "3 · Recovered carbon copy", content:
- P("DEPARTMENTAL CUSTODY SERVICE
"
- + "R-143 / UNDELIVERABLE MATERIAL
19 SEPTEMBER 1987")
- + P("Window Six and contents entered into temporary agency custody, pending collection.")
- + P("Consignment must not be:")
- + UL(["opened;", "destroyed;", "redirected; or", "returned to sender."])
- + P("Status remains PENDING COLLECTION until receipted by a serving officer.")
- + P("Pages attached: 14, 22.")
-},
-
-{ name: "4 · The opened letter", content:
- P("CIVIL CONTINUITY REGISTRY
NOTICE OF REDIRECTION")
- + P("Dear Ms Lorna Vale,")
- + P("The Registry regrets that the name and civic person presently delivered to:")
- + P("14 MERE ROAD, COVENTRY")
- + P("has previously and continuously been delivered to:")
- + P("14 MERE ROAD, COVENTRY")
- + P("This duplication cannot continue after the Last Collection.")
- + P("Unless an authorised custodian accepts the disputed entry, one delivery will be "
- + "cancelled without further notice.")
- + P("Please do not destroy this notice. This notice contains material belonging to "
- + "the addressee.")
-},
-
-{ name: "5 · Window Six procedure card", content:
- H(2, "WINDOW SIX")
- + OL(["State COLLECTION or DELIVERY.",
- "Give name, department and case reference.",
- "Remain on the public side of the counter.",
- "Do not open or read mail not addressed to you.",
- "Every consignment removed requires a receipt.",
- "The master copy receives the crown date stamp."])
- + P("UNRECEIPTED MATERIAL WILL BE CANCELLED.")
-},
-
-{ name: "6 · R-143-A receipt", content:
- P("DEPARTMENTAL CUSTODY SERVICE
"
- + "R-143-A / PENDING INTER-JURISDICTIONAL REGISTER")
- + P("The undersigned accept temporary custody of the disputed folio and the living civic "
- + "identities recorded within it, without determining final ownership, origin or "
- + "jurisdiction.")
- + TABLE(["Requirement", "Name / finding", "Signature"], [
- ["Serving officer accepting responsibility", "", ""],
- ["Containment established", "", ""],
- ["Terms agreed with transferring authority", "", ""],
- ["Living status medically certified", "", ""],
- ["Window Six repaired and current date applied", "", ""],
- ["Crown master stamp recovered", "", ""]
+{ name: "2 · Who can do what", content:
+ P("Everyone on this operation can fight. That is deliberate: nobody sits out a fight on "
+ + "a first case.")
+ + TABLE(["Agent", "Firearm", "Melee", "Brawl", "Dodge"], [
+ ["Nia Mercer — Field Lead", "Pistol 55%", "Knife 45%", "40%", "45%"],
+ ["Callum Pike — Containment", "Rifle 55%", "Entry bar 50% · Shield 45%", "50%", "45%"],
+ ["Mara Venn — Overwatch", "Rifle 58%", "Cudgel 45%", "40%", "50%"],
+ ["Owen Dey — Engineer", "Pistol 42%", "Cudgel 45%", "40%", "40%"],
+ ["Dr Elin Shaw — Doctor", "Pistol 42%", "Knife 40%", "40%", "40%"],
+ ["Sennen Roake — Criminal", "Pistol 42%", "Knife 50%", "45%", "50%"]
])
- + P("Case status: pending adjudication
"
- + "Collection date: ______________
"
- + "Crown stamp: ______________")
+ + P("Use the firearm across the platform and down the long aisle. "
+ + "Switch to melee when something closes on you, when there are echoes "
+ + "in the way of your shot, or when you are guarding somebody at arm's length.")
+ + P("These are floors, not your whole character. Owen is still far better at engineering "
+ + "than at fighting, and Elin is still the only doctor.")
+},
+
+{ name: "3 · The closed platform", content:
+ PRE("STREET\n"
+ + " |\n"
+ + "140 STEPS\n"
+ + " |\n"
+ + "STEEL ACCESS GATE -- wet floor -- LIVE TRACTION RAIL\n"
+ + " |\n"
+ + "WEST TUNNEL 40 m BLACK PLATFORM EAST TUNNEL\n"
+ + "=================[ rear ][ centre ][ front/cab ]=================\n"
+ + " stairs/gate pillars every 8 m signal box\n"
+ + " team entry partial cover train doors")
+ + P("The train stands at the platform for about ninety seconds.")
+},
+
+{ name: "4 · The train", content:
+ PRE("[ REAR SERVICE ] -- [ PASSENGER CARRIAGE ] -- [ FRONT / DRIVER'S CAB ]\n"
+ + " engineers echo passengers route cabinet\n"
+ + " locked door connecting doors field regulator\n"
+ + " first-aid box long firing lane manual brake")
+},
+
+{ name: "5 · Martin Cole's shutdown", content:
+ OL(["Open the route cabinet.",
+ "Reverse the field regulator.",
+ "Take out the brass route plate.",
+ "Pull the manual brake when the indicator says HOME."])
+ + READ("Not before HOME. If you brake between stations, it opens the doors.")
+},
+
+{ name: "6 · The final sequence", content:
+ P("All of this happens at once, while the train runs at Final Terminus.")
+ + TABLE(["Job", "Who", "Done"], [
+ ["Open the route cabinet", "Criminal", "☐"],
+ ["Reverse the regulator — 1", "Engineer", "☐"],
+ ["Reverse the regulator — 2", "Engineer", "☐"],
+ ["Reverse the regulator — 3", "Engineer", "☐"],
+ ["Free the manual brake", "Containment", "☐"],
+ ["Call HOME", "Field Lead", "☐"],
+ ["Keep the specialists alive", "Overwatch", "☐"],
+ ["Keep the casualties alive", "Doctor", "☐"]
+ ])
+ + P("Terminus: 0 ☐ · 1 ☐ · 2 ☐ · 3 ☐ · 4 ☐ · 5 ☐ · 6 ☐")
},
{ name: "7 · Debrief slip", content:
- P("Complete one sentence before speaking to the debriefing officer.")
- + UL(["The case stopped matching our orders when ____________________.",
- "The person or thing I protected was ____________________.",
- "The fact I would remove from the official report is ____________________.",
- "The name we knowingly left off the form was ____________________."])
+ P("One line each, before you talk to the debriefing officer.")
+ + UL(["When the doors opened, I got on because ____________________.",
+ "The moment I thought we might not come home was ____________________.",
+ "What I saw through the train window was ____________________."])
}
];
/* ---------------------------------------------------------------- */
-/* The team, the cast, the case file and the credential */
+/* The team */
/* ---------------------------------------------------------------- */
/**
- * The six pregens. Built through the same buildActor path the shipped pregens use, so
- * they arrive with skills, kit, hit points and a token exactly like any other agent.
- * Ratings are fixed rather than rolled so every table gets the same starter.
+ * Six pregens. `floors` are the scenario's deliberate starter minimums: every agent
+ * carries a firearm, a melee weapon, Brawl and Dodge at a usable rating so nobody
+ * becomes a spectator when initiative is rolled. They only ever RAISE a rating, so a
+ * posting already better at something stays better.
*/
export const STARTER_TEAM = [
- { key: "pw_nia", name: "Nia Mercer", role: "lead", rank: "Field Officer",
+ { key: "bp_nia", name: "Nia Mercer", role: "lead", rank: "Field Officer",
chosenStyle: "reflex", species: "baseline", age: { chronological: 36, physiological: 36 },
ch: { str: 11, siz: 12, con: 13, int: 14, pow: 13, dex: 15, cha: 13, edu: 12 },
talents: ["first_through", "read_room"],
- spotlight: "Keep the operation coherent, lead the case phases and sign for the final decision.",
- complication: "Nia's cover identity says she worked a Christmas shift at this office in 1987. She was born in 1989.",
- note: "Give Nia the highest Tradecraft on the team. Being in charge means deciding who acts, not making every roll." },
+ extraWeapons: ["dagger"],
+ floors: { "firearm:pistol": 55, "melee_weapon:knife": 45, brawl: 40, dodge: 45 },
+ spotlight: "Run the team, split it when it needs splitting, and call HOME at the right second.",
+ note: "Being in charge is deciding who acts — not making every roll yourself." },
- { key: "pw_callum", name: "Callum Pike", role: "heavy", rank: "Field Officer",
+ { key: "bp_callum", name: "Callum Pike", role: "heavy", rank: "Field Officer",
chosenStyle: "reflex", species: "baseline", age: { chronological: 34, physiological: 34 },
ch: { str: 16, siz: 15, con: 15, int: 11, pow: 11, dex: 13, cha: 10, edu: 10 },
talents: ["hard_landing", "first_through"],
- spotlight: "Hold the corridor, protect the folio carrier and restrain rather than destroy.",
- complication: "The Cancellation Man wears Callum's handwriting on several of its envelopes.",
- note: "Callum starts with the riot shield SLUNG. Raising it is a real decision: torso and arms improve, and his own shooting takes -20%." },
+ floors: { "firearm:rifle": 55, "melee_weapon:prybar": 50, "melee_weapon:shield": 45,
+ brawl: 50, dodge: 45 },
+ spotlight: "Front rank. Hold the door, cover the people doing the work, free the brake.",
+ note: "Your riot shield starts SLUNG. Raising it protects torso and arms and costs you -20% on your own shooting. That is a decision, every time." },
- { key: "pw_amira", name: "Amira Bell", role: "settlements", rank: "Field Officer",
- chosenStyle: "deliberate", species: "baseline", age: { chronological: 41, physiological: 41 },
- ch: { str: 10, siz: 11, con: 12, int: 15, pow: 14, dex: 11, cha: 16, edu: 15 },
- talents: ["read_room", "debriefer"],
- spotlight: "Understand what the clerks need and make an arrangement reality will accept.",
- complication: "Amira knows the agency can lawfully abandon everyone on the other side. Her orders strongly imply that it should.",
- note: "Amira runs READ, SIGNAL and OFFER. The correct offer is a receipt, not a bribe." },
+ { key: "bp_mara", name: "Mara Venn", role: "overwatch", rank: "Field Officer",
+ chosenStyle: "deliberate", species: "baseline", age: { chronological: 38, physiological: 38 },
+ ch: { str: 12, siz: 12, con: 13, int: 13, pow: 12, dex: 16, cha: 11, edu: 12 },
+ talents: ["first_through", "hard_landing"],
+ extraWeapons: ["cudgel"],
+ floors: { "firearm:rifle": 58, "melee_weapon:club": 45, brawl: 40, dodge: 50 },
+ spotlight: "Own the long line — the length of the platform, then the length of the aisle.",
+ note: "Deliberate marksmanship: better at range, worse if you move and fire. Shooting past the echoes is -20%." },
- { key: "pw_elin", name: "Dr Elin Shaw", trade: "doctor", rank: "Probationary",
- chosenStyle: "deliberate", species: "baseline", age: { chronological: 31, physiological: 31 },
- ch: { str: 10, siz: 11, con: 11, int: 16, pow: 12, dex: 12, cha: 13, edu: 17 },
- talents: ["triage", "unblinking"],
- spotlight: "Establish that both Lorna Vales are living people, treat injuries and certify the disputed names.",
- complication: "One delivered letter is a correctly formatted notice of Elin's death, dated nine years from now.",
- note: "A proper doctor and a new Custodian. Her Medicine is excellent and her induction — Tradecraft, Anomaly Lore, Dodge, Pistol — is not." },
-
- { key: "pw_owen", name: "Owen Dey", trade: "engineer", rank: "Probationary",
+ { key: "bp_owen", name: "Owen Dey", trade: "engineer", rank: "Probationary",
chosenStyle: "reflex", species: "baseline", age: { chronological: 29, physiological: 29 },
ch: { str: 12, siz: 12, con: 12, int: 17, pow: 11, dex: 14, cha: 10, edu: 15 },
talents: ["hard_landing", "requisition"],
- spotlight: "Diagnose Window Six, repair its franking mechanism and keep the crossing stable.",
- complication: "The mechanism has been repaired before, using Owen's preferred soldering pattern.",
- note: "Diagnosing the franker early gives +20% on the repair that closes the case." },
+ extraWeapons: ["cudgel"],
+ floors: { "firearm:pistol": 42, "melee_weapon:club": 45, brawl: 40, dodge: 40 },
+ spotlight: "Kill the rail on the way in, then reverse the regulator with the Conductor on top of you.",
+ note: "Martin's instructions are worth +20%. Get them out of him before you touch anything." },
- { key: "pw_sennen", name: "Sennen Roake", trade: "criminal", rank: "Probationary",
+ { key: "bp_elin", name: "Dr Elin Shaw", trade: "doctor", rank: "Probationary",
+ chosenStyle: "deliberate", species: "baseline", age: { chronological: 31, physiological: 31 },
+ ch: { str: 10, siz: 11, con: 11, int: 16, pow: 12, dex: 12, cha: 13, edu: 17 },
+ talents: ["triage", "unblinking"],
+ extraWeapons: ["dagger"],
+ floors: { "firearm:pistol": 42, "melee_weapon:knife": 40, brawl: 40, dodge: 40 },
+ spotlight: "Keep Leah alive, keep the team upright, and be the reason both engineers walk out.",
+ note: "A wound gets ONE First Aid attempt. Choose when to spend it." },
+
+ { key: "bp_sennen", name: "Sennen Roake", trade: "criminal", rank: "Probationary",
chosenStyle: "reflex", species: "baseline", age: { chronological: 27, physiological: 27 },
ch: { str: 11, siz: 10, con: 12, int: 13, pow: 11, dex: 17, cha: 14, edu: 9 },
talents: ["cover_work", "clean_scene"],
- spotlight: "Enter the cage without breaking its rules and recover the master date stamp.",
- complication: "The mundane police database recognises Sennen's fingerprints under the name of a postal worker who died in the 1987 fire.",
- note: "The cage is the tutorial for doing a criminal thing procedurally: the drawer opens from the public side, and that matters." }
+ extraWeapons: ["dagger"],
+ floors: { "firearm:pistol": 42, "melee_weapon:knife": 50, brawl: 45, dodge: 50 },
+ spotlight: "Get the locked service door open, then the route cabinet, without shooting either.",
+ note: "Shooting a lock works and advances the clock. Everything you open quietly is time somebody else gets to use." }
];
-/** The cast, as statblocks a GM can drag onto a scene. */
+/** The cast: two casualties, the inspectors, and the thing at the controls. */
export const STARTER_CAST = [
- { key: "pw_evelyn", name: "Evelyn Vale", role: "FOLKLORE. Night supervisor, Civil Continuity Registry",
- chosenStyle: "deliberate", naturalArmour: 0, species: "baseline",
- ch: { str: 10, siz: 11, con: 12, int: 15, pow: 16, dex: 11, cha: 15, edu: 16 },
- skills: [{ fam: "insight", spec: "", val: 75 }, { fam: "status", spec: "", val: 70 },
- { fam: "bargain", spec: "", val: 65 }, { fam: "knowledge", spec: "", val: 70 },
- { fam: "listen", spec: "", val: 60 }, { fam: "persuade", spec: "", val: 55 }],
- weapons: ["punch"],
- tactics: "Formal, tired and exact. She wants a valid receipt more than she wants rescue, and she has wanted it for thirty-nine years. She does not lie; she answers only the question procedurally put to her, which is not the same thing and the players will notice. POWER: THE COUNTER. Anything said from behind her counter by somebody who does not work there is treated as unsaid — no roll, no argument. Agents who work this out can use it deliberately." },
-
- { key: "pw_lorna_m", name: "Lorna Vale (mundane)", role: "FOLKLORE. Council payroll officer, forty-two",
+ { key: "bp_leah", name: "Leah Hall", role: "CIVILIAN. Network Rail engineer, trapped",
chosenStyle: "reflex", naturalArmour: 0, species: "baseline",
- ch: { str: 10, siz: 11, con: 11, int: 12, pow: 11, dex: 11, cha: 12, edu: 12 },
- skills: [{ fam: "knowledge", spec: "", val: 45 }, { fam: "insight", spec: "", val: 40 },
- { fam: "status", spec: "", val: 35 }, { fam: "listen", spec: "", val: 40 }],
+ ch: { str: 12, siz: 11, con: 11, int: 13, pow: 11, dex: 12, cha: 11, edu: 13 },
+ skills: [{ fam: "repair", spec: "mechanical", val: 60 },
+ { fam: "engineering", spec: "field_systems", val: 55 },
+ { fam: "spot", spec: "", val: 40 }],
weapons: ["punch"],
- tactics: "Angry about being treated as evidence. Wants her life AND the truth, and will not accept being handed either one instead of the other. She lies when she says she has no curiosity about the other Lorna's daughter." },
+ tactics: "Pinned under a folded maintenance seat. Left leg disabled, START HER AT 4 HIT POINTS, bleeding slowly. Lifting the seat is Athletics or an entry bar; stopping the bleed is First Aid; moving her safely wants the recovery strop or two people. She is calm, in a great deal of pain, and more worried about Martin than about herself." },
- { key: "pw_lorna_r", name: "Lorna Vale (Registry)", role: "FOLKLORE. Registry clerk, thirty-nine years on one shift",
+ { key: "bp_martin", name: "Martin Cole", role: "CIVILIAN. Network Rail engineer, concussed",
chosenStyle: "reflex", naturalArmour: 0, species: "baseline",
- ch: { str: 10, siz: 11, con: 11, int: 12, pow: 13, dex: 11, cha: 12, edu: 13 },
- skills: [{ fam: "knowledge", spec: "", val: 55 }, { fam: "insight", spec: "", val: 50 },
- { fam: "listen", spec: "", val: 45 }, { fam: "research", spec: "", val: 50 }],
+ ch: { str: 12, siz: 12, con: 12, int: 12, pow: 10, dex: 11, cha: 12, edu: 13 },
+ skills: [{ fam: "repair", spec: "mechanical", val: 55 },
+ { fam: "engineering", spec: "field_systems", val: 50 },
+ { fam: "first_aid", spec: "", val: 35 }],
weapons: ["punch"],
- tactics: "Wants the shift to end without taking her existence with it. Remembers a daughter born in the Near world. She lies when she says she has never sent a letter to the mundane world before. Asked which Lorna is real, either will answer: \"Both, until you file otherwise.\"" },
+ tactics: "Concussed, START HIM AT 7 HIT POINTS, awake and frightened. He knows the four steps: open the cabinet, reverse the regulator, remove the plate, brake at HOME. Calm or treat him and he says it clearly; otherwise he blurts it in the wrong order and they still get all four. He will not leave without Leah." },
- { key: "pw_gareth", name: "Gareth Moss", role: "FOLKLORE. Demolition site manager",
- chosenStyle: "reflex", naturalArmour: 0, species: "baseline",
- ch: { str: 13, siz: 13, con: 13, int: 12, pow: 10, dex: 11, cha: 12, edu: 11 },
- skills: [{ fam: "repair", spec: "mechanical", val: 55 }, { fam: "status", spec: "", val: 40 },
- { fam: "spot", spec: "", val: 40 }, { fam: "bargain", spec: "", val: 45 }],
- weapons: ["punch"],
- tactics: "Practical, not obstructive. Wants a signed instruction and somebody else liable for the delay. Treat him as a professional and he gives up everything he knows without a roll. He lies exactly once: when he says he never looked through Window Six." },
+ { key: "bp_inspector", name: "Ticket inspector", role: "HORROR. Part of the service, not a person",
+ chosenStyle: "reflex", naturalArmour: 2, species: "baseline",
+ ch: { str: 13, siz: 12, con: 13, int: 7, pow: 12, dex: 13, cha: 4, edu: 5 },
+ skills: [{ fam: "brawl", spec: "", val: 45 },
+ { fam: "melee_weapon", spec: "club", val: 50 },
+ { fam: "dodge", spec: "", val: 40 },
+ { fam: "spot", spec: "", val: 45 }],
+ weapons: ["cudgel"],
+ tactics: "Dark blue uniform, blank face under a peaked cap, wooden ticket punch held like a baton. USE FOUR ON THE PLATFORM AND THREE IN THE CARRIAGE. It walks at the nearest agent still standing and ignores anyone already down — it is not cruel, it is enforcing a rule. It dodges and never uses a firearm. In the carriage it tries to seize an engineer and mark them RETURNED. It withdraws at 2 hit points or when restrained, and on the platform the last one leaves once three are down. POWER: NO DESTINATION SHOWN. It will not strike anyone holding out a valid ticket, a warrant card presented flat, or any other document formally offered — one round of grace, and waiting costs it nothing." },
- { key: "pw_cancellation", name: "The Cancellation Man", role: "HORROR. A process with a body",
+ { key: "bp_conductor", name: "The Conductor", role: "HORROR. The service itself, wearing a uniform",
chosenStyle: "reflex", naturalArmour: 4, species: "baseline",
- ch: { str: 16, siz: 14, con: 16, int: 8, pow: 17, dex: 14, cha: 3, edu: 5 },
- skills: [{ fam: "brawl", spec: "", val: 65 }, { fam: "dodge", spec: "", val: 45 },
- { fam: "stealth", spec: "", val: 60 }, { fam: "spot", spec: "", val: 70 },
- { fam: "athletics", spec: "", val: 55 }],
- weapons: ["grasp"],
- tactics: "It unfolds from returned envelopes, red twine and black cancellation ink, and every address on it belongs to somebody in the room. It wants the DISPUTED FOLIO, not casualties: it moves to the carrier and ignores anyone who does not obstruct it. Rename its attack the CANCELLATION STAMP. POWER: UNRECEIPTED. Anything it takes that has no receipt attached is gone — not destroyed, never delivered. It withdraws at 2 hit points, when restrained, or the moment it is handed a properly bagged and receipted folio. Killing it is temporary; it reforms at the next clock advance. That is the lesson." }
+ ch: { str: 16, siz: 14, con: 16, int: 9, pow: 17, dex: 14, cha: 3, edu: 6 },
+ skills: [{ fam: "brawl", spec: "", val: 65 },
+ { fam: "melee_weapon", spec: "club", val: 60 },
+ { fam: "dodge", spec: "", val: 45 },
+ { fam: "spot", spec: "", val: 60 }],
+ weapons: ["cudgel"],
+ tactics: "Tall, black uniform, polished face with nothing on it, one gloved hand fused around the throttle. It attacks whoever is working a control — the route plate first, then the regulator, then the brake — and does not chase anybody retreating toward the rear. It can be restrained and cannot be reasoned with. AT 2 HIT POINTS IT IS DRIVEN BACK, NOT KILLED: it keeps trying to reach the controls at half speed. POWER: ALL PASSENGERS REMAIN ABOARD. Once per fight, as its action, it shuts and seals every door in one carriage. Opening one costs an action and Athletics, an entry bar or the bolt-cutters." }
];
-/** The credential, as a real item rather than a paragraph in the brief. */
-export const STARTER_AUTHORITY = {
- key: "pw_price",
- name: "Imogen Price — Senior Asbestos Surveyor",
- holderName: "Imogen Price",
- holderRole: "Senior Asbestos Surveyor",
- issuer: "Halcyon Environmental Compliance Ltd",
- scope: "routine",
- scopeNote: "Routine site inspection at a controlled demolition site.",
- trust: 2,
- redFlags: [
- "Price signs in at every controlled site.",
- "Price never conducts an inspection alone.",
- "Price photographs every opened door."
- ],
- description:
- "Inside its scope this works and costs 1 Trust: a surveyor walking a condemned "
- + "building is not intrusion, it is Tuesday.
"
- + "Entering the sealed records cage or altering the demolition schedule is one "
- + "tier beyond — roll, at −20% for each red flag broken.
"
- + "A fumble burns it: the real Imogen Price telephones the site manager.
"
-};
+/** No credential on this case — the station is already closed to the public. */
+export const STARTER_AUTHORITY = null;
-/**
- * The groups the team can talk to, as DATA rather than as something the pack builder
- * invents. `resolveContact` looks up a group ITEM on the case file and returns null
- * without one — a standing record alone leaves READ, SIGNAL and OFFER silently switched
- * off, which is how this scenario first shipped, with the contact scene as its
- * centrepiece. The pairing is asserted in check-behaviour against this file.
- */
-export const STARTER_GROUPS = [{
- groupId: "returned_clerks",
- name: "The Returned Clerks",
- population: 6,
- npcTemplateId: "pw_evelyn",
- description:
- "Six clerks in 1987 uniforms under green lamps, none of them aged. They have been "
- + "on the same shift for thirty-nine years, and they are not invaders — they are "
- + "waiting to be relieved.
",
- taboos: [
- { id: "sealed_mail",
- text: "Never open, read aloud or name the addressee of mail that is not yours. A sealed identity belongs to its recipient.",
- penaltyIfViolated: -30 },
- { id: "the_counter",
- text: "The public side of the counter is the public side. Speaking from behind it is not rudeness — it is not speaking at all.",
- penaltyIfViolated: -20 }
- ],
- desires: [
- { id: "relief",
- text: "To be relieved. A recognised authority to receipt the disputed folio so the shift can close.",
- offerBonusStep: "easy" }
- ],
- correctOfferForm:
- "A signed receipt taking the disputed folio into recognised custody, passed through "
- + "Window Six from the public side of the counter, quoting case R-143.",
- rewardsByStanding: {
- "1": [{ id: "pw1_explain",
- text: "Evelyn explains that the register needs custody and certification, and what that means.",
- effect: { type: "reveal", note: "The folio needs a custodian, a medical certification that the duplicates are living persons, and a current date stamp." } }],
- "2": [{ id: "pw2_folio",
- text: "Evelyn explains the clean closure and will release the folio once the six requirements are met.",
- effect: { type: "reveal", note: "R-143-A: a pending inter-jurisdictional register. Six signatures, one per agent." } },
- { id: "pw2_stamp",
- text: "She says which drawer holds the crown master stamp, and that it opens from the public side.",
- effect: { type: "skillBonus", skillFamilyId: "sleight_of_hand", value: 20, phases: [3] } }],
- "3": [{ id: "pw3_help",
- text: "The night shift helps. Two clerks hold the folio steady while the engineer works.",
- effect: { type: "waiveCost", phase: 3 } }]
- }
-}];
+/** No social subsystem on this case, so no groups and no standings. */
+export const STARTER_GROUPS = [];
-/** The case file, already set to the scenario's opening state. */
+/** The case file, already at the scenario's opening state. */
export const STARTER_CASE = {
- key: "pw_case",
- name: "PW-01 · Rookery Heath",
- containment: 8, resources: 8, clearance: 6,
- destination: "Rookery Heath Delivery Office, Coventry",
+ key: "bp_case",
+ name: "BP-017 · Black Platform",
+ containment: 9, resources: 8, clearance: 5,
+ destination: "Black Platform, beneath Wych Street, London",
+ standings: [],
notes:
- "Last Collection clock 0 / 4. Advance it on a fumbled phase, twenty "
- + "minutes of argument without action, a loud weapon fired in the mundane building, the "
- + "credential burned, or the clerks' taboo violated after it has been learned.
"
- + "Not for careful investigation. Never for careful investigation.
",
- standings: ["returned_clerks"],
+ "Terminus clock 0 / 6. Move it after each big scene, on a fumbled "
+ + "phase, when the group stands arguing in the middle of something dangerous — and "
+ + "once every round during the cab sequence.
"
+ + "0 Due · 1 Arriving · 2 Departed (mark Across) · 3 Between (−1 Coherence, once) · "
+ + "4 Inspection · 5 Final approach (−10% physical) · 6 Terminus.
"
+ + "Issued for this case: six ear defenders, two locator tags, a "
+ + "recovery strop, insulated bolt-cutters, and a paper diagram of the platform.
",
legs: [
- { label: "Approach — the demolition cordon", kindId: "approach", difficulty: "average", mod: 0 },
- { label: "Investigation — the dead-letter cage", kindId: "cordon", difficulty: "average", mod: -5 },
- { label: "Containment — the night shift", kindId: "containment", difficulty: "difficult", mod: 0 },
- { label: "Extraction — closing the register", kindId: "extraction", difficulty: "average", mod: -15 }
+ { label: "Approach — down to Black Platform", kindId: "approach", difficulty: "average", mod: 0 },
+ { label: "Confrontation — take the platform", kindId: "cordon", difficulty: "average", mod: -5 },
+ { label: "Containment — seize the train", kindId: "containment", difficulty: "difficult", mod: 0 },
+ { label: "Extraction — stop at HOME", kindId: "extraction", difficulty: "average", mod: -15 }
]
};
diff --git a/tools/update-readme.mjs b/tools/update-readme.mjs
index 6c0d163..01ac927 100644
--- a/tools/update-readme.mjs
+++ b/tools/update-readme.mjs
@@ -40,7 +40,7 @@ const box = `| | |
| **Bestiary** | ${NPCS.length} statblocks across folklore, horror and the far side |
| **Eras** | modern, antique, future, anomalous — the agency is old and never abolishes anything |
| **Icons** | ${Object.keys(ICON_MAP).length}, all generated from \`tools/icons.mjs\`; no external art |
-| **Starter scenario** | **PAPER WINDOW**, a six-player introductory case, importable as a single Adventure |
+| **Starter scenario** | **BLACK PLATFORM**, a six-player action starter, importable as a single Adventure |
| **Compendia** | pregens, NPCs, skills, weapons, armour, gear, vehicles, powers, tables, rules, the starter |
| **Rules journal** | ${RULES_PAGES.length} pages, generated from \`rules.mjs\` so the book cannot drift from the code |`;