diff --git a/DECISIONS.md b/DECISIONS.md
index 1564afc..b1a339d 100644
--- a/DECISIONS.md
+++ b/DECISIONS.md
@@ -889,3 +889,8 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
- **A band counts as flyable if some ship can carry its minimum**, and a fit job's mass is capped at the chosen ship's capacity; so a Freighter also gets work from a band whose top it can't carry.
- **Fit destinations are checked at the board's start time**, like the contract's own distance. A route at the edge of a ship's range could drift out during the 10-minute period; dispatch validation still decides.
- **An uncrewed ship counts with no skills**, as if crewed by a fresh hire, so buying a ship immediately shapes the next board.
+
+## Follow-up after Phase 10F: deadheads in the logbook
+
+- **Found while checking deadhead end to end** (user request): a deadhead's "While you were away" entry read "+0 cr +0 XP" in success green. `ResolutionSummary` gains `deadhead` (the dispatch had no contract) and `fuelCost` (paid at departure); the entry now reads "Deadhead · 230 cr fuel" with a flight icon. Tests: `away-summary.test.tsx`, `reposition.test.ts`, `loan.test.ts`.
+- **The missing favicon (a harmless 404) is left for later**, by the user's choice; Phase 11's PWA icons are the natural place.
diff --git a/apps/server/src/game/player-state.ts b/apps/server/src/game/player-state.ts
index 7440060..9f7fd5a 100644
--- a/apps/server/src/game/player-state.ts
+++ b/apps/server/src/game/player-state.ts
@@ -107,6 +107,8 @@ export async function loadPlayerState(
originId: d.record.originId,
destinationId: d.record.destinationId,
tutorial: d.tutorial,
+ deadhead: d.record.contract === null,
+ fuelCost: d.record.fuelCost,
credits: d.resolution?.credits ?? 0,
loanRepayment: d.resolution?.loanRepayment ?? 0,
pilotXp: d.resolution?.pilotXp ?? 0,
diff --git a/apps/server/test/loan.test.ts b/apps/server/test/loan.test.ts
index ca789da..53a327b 100644
--- a/apps/server/test/loan.test.ts
+++ b/apps/server/test/loan.test.ts
@@ -49,6 +49,7 @@ describe("loan repayment on delivery", () => {
loanBalance: 5000 - repayment,
});
expect(state.summaries[0]).toMatchObject({
+ deadhead: false,
credits: 2000 - repayment,
loanRepayment: repayment,
});
diff --git a/apps/server/test/reposition.test.ts b/apps/server/test/reposition.test.ts
index df37e98..6efed35 100644
--- a/apps/server/test/reposition.test.ts
+++ b/apps/server/test/reposition.test.ts
@@ -35,6 +35,14 @@ describe("deadhead dispatch", () => {
expect(state.ships[0]).toMatchObject({ status: "idle", locationId: "moon" });
expect(state.pilots[0]).toMatchObject({ status: "idle", locationId: "moon", xp: 0 });
expect(state.company.credits).toBe(10_000 - dispatch.fuelCost);
+ // The logbook marks it as a deadhead with its fuel (no payout to show).
+ expect(state.summaries).toHaveLength(1);
+ expect(state.summaries[0]).toMatchObject({
+ deadhead: true,
+ fuelCost: dispatch.fuelCost,
+ credits: 0,
+ pilotXp: 0,
+ });
});
it("refuses an undiscovered destination and changes nothing", async () => {
diff --git a/apps/web/src/components/AwaySummary.tsx b/apps/web/src/components/AwaySummary.tsx
index e35166e..d101bee 100644
--- a/apps/web/src/components/AwaySummary.tsx
+++ b/apps/web/src/components/AwaySummary.tsx
@@ -47,13 +47,21 @@ export function AwaySummary() {
{locationName(s.originId)} → {locationName(s.destinationId)}
-
- = 0 ? "text-emerald-300" : "text-rose-300"}>
- {s.credits >= 0 ? "+" : ""}
- {formatCredits(s.credits)}
-
- +{s.pilotXp} XP
-
+ {s.deadhead ? (
+ // An empty flight earns nothing: say what it was and what it cost.
+
+
+ Deadhead · {formatCredits(s.fuelCost)} fuel
+
+ ) : (
+
+ = 0 ? "text-emerald-300" : "text-rose-300"}>
+ {s.credits >= 0 ? "+" : ""}
+ {formatCredits(s.credits)}
+
+ +{s.pilotXp} XP
+
+ )}
{s.loanRepayment > 0 && (
{formatCredits(s.loanRepayment)} went to the lender
diff --git a/apps/web/test/away-goals.test.tsx b/apps/web/test/away-goals.test.tsx
index 580af6e..36debc5 100644
--- a/apps/web/test/away-goals.test.tsx
+++ b/apps/web/test/away-goals.test.tsx
@@ -32,6 +32,8 @@ function returningPlayer(acknowledged: boolean): PlayerState {
destinationId: "moon",
tutorial: false,
credits: 538,
+ deadhead: false,
+ fuelCost: 0,
loanRepayment: 230,
pilotXp: 10,
discoveredLocationId: null,
diff --git a/apps/web/test/away-summary.test.tsx b/apps/web/test/away-summary.test.tsx
index cbc28fd..fc026bc 100644
--- a/apps/web/test/away-summary.test.tsx
+++ b/apps/web/test/away-summary.test.tsx
@@ -24,6 +24,8 @@ const summaries: ResolutionSummary[] = [
destinationId: "mars",
tutorial: false,
credits: 51_200,
+ deadhead: false,
+ fuelCost: 0,
loanRepayment: 0,
pilotXp: 32,
discoveredLocationId: "mars",
@@ -39,6 +41,8 @@ const summaries: ResolutionSummary[] = [
destinationId: "earth",
tutorial: true,
credits: 240,
+ deadhead: false,
+ fuelCost: 0,
loanRepayment: 0,
pilotXp: 10,
discoveredLocationId: null,
@@ -49,6 +53,32 @@ const summaries: ResolutionSummary[] = [
];
describe("while you were away", () => {
+ it("shows a deadhead as an empty flight with its fuel, not as a +0 payout", async () => {
+ const deadhead: ResolutionSummary = {
+ ...summaries[0]!,
+ dispatchId: "d3",
+ destinationId: "moon",
+ deadhead: true,
+ fuelCost: 230,
+ credits: 0,
+ pilotXp: 0,
+ discoveredLocationId: null,
+ };
+ renderApp(
+ fakeApi({
+ getPlayerState: () =>
+ samplePlayerState({ tutorial: null, ships: [ship], summaries: [deadhead] }),
+ }),
+ );
+ const dialog = await screen.findByRole("dialog", { name: "While you were away" });
+ const entry = within(dialog).getAllByRole("listitem")[0]!;
+ expect(entry).toHaveTextContent("Dusty Mule");
+ expect(entry).toHaveTextContent("Earth → Moon");
+ expect(entry).toHaveTextContent("Deadhead · 230 cr fuel");
+ expect(entry).not.toHaveTextContent("+0 cr");
+ expect(entry).not.toHaveTextContent("XP");
+ });
+
it("lists each completed delivery, and dismissing acknowledges them and hides the dialog", async () => {
let acknowledged = false;
const api = fakeApi({
diff --git a/apps/web/test/deal-modal.test.tsx b/apps/web/test/deal-modal.test.tsx
index ee5fd28..50b190f 100644
--- a/apps/web/test/deal-modal.test.tsx
+++ b/apps/web/test/deal-modal.test.tsx
@@ -82,6 +82,8 @@ describe("the deal", () => {
destinationId: "earth",
tutorial: true,
credits: 96,
+ deadhead: false,
+ fuelCost: 0,
loanRepayment: 0,
pilotXp: 10,
discoveredLocationId: null,
diff --git a/apps/web/test/hq.test.tsx b/apps/web/test/hq.test.tsx
index 18c97b7..69ca5aa 100644
--- a/apps/web/test/hq.test.tsx
+++ b/apps/web/test/hq.test.tsx
@@ -146,6 +146,8 @@ describe("loan on HQ", () => {
destinationId: "moon",
tutorial: false,
credits: 538,
+ deadhead: false,
+ fuelCost: 0,
loanRepayment: 230,
pilotXp: 10,
discoveredLocationId: null,
diff --git a/apps/web/test/rest-summary-hq.test.tsx b/apps/web/test/rest-summary-hq.test.tsx
index c6c1f70..648a53d 100644
--- a/apps/web/test/rest-summary-hq.test.tsx
+++ b/apps/web/test/rest-summary-hq.test.tsx
@@ -33,6 +33,8 @@ const summary = (extra: Partial): ResolutionSummary => ({
destinationId: "moon",
tutorial: false,
credits: 500,
+ deadhead: false,
+ fuelCost: 0,
loanRepayment: 0,
pilotXp: 10,
discoveredLocationId: null,
diff --git a/apps/web/test/tutorial-board.test.tsx b/apps/web/test/tutorial-board.test.tsx
index b1325f3..a4ae0a7 100644
--- a/apps/web/test/tutorial-board.test.tsx
+++ b/apps/web/test/tutorial-board.test.tsx
@@ -149,6 +149,8 @@ describe("scripted delivery summary", () => {
destinationId: "moon",
tutorial: true,
credits: 461,
+ deadhead: false,
+ fuelCost: 0,
loanRepayment: 0,
pilotXp: 10,
discoveredLocationId: null,
diff --git a/packages/schema/src/entities.ts b/packages/schema/src/entities.ts
index 95ef86d..fe20412 100644
--- a/packages/schema/src/entities.ts
+++ b/packages/schema/src/entities.ts
@@ -126,6 +126,10 @@ export const resolutionSummarySchema = z.object({
originId: idSchema,
destinationId: idSchema,
tutorial: z.boolean(),
+ /** An empty reposition flight: no cargo, no pay (CR-002). */
+ deadhead: z.boolean(),
+ /** Fuel paid at departure. */
+ fuelCost: credits.nonnegative(),
/** Credits added after pilot pay and loan repayment. */
credits,
/** Credits the lender took from this payout (decision 0.5.32). */
diff --git a/packages/schema/test/schemas.test.ts b/packages/schema/test/schemas.test.ts
index ba8f725..18350e2 100644
--- a/packages/schema/test/schemas.test.ts
+++ b/packages/schema/test/schemas.test.ts
@@ -114,6 +114,8 @@ const summary = {
destinationId: "earth",
tutorial: true,
credits: 240,
+ deadhead: false,
+ fuelCost: 0,
loanRepayment: 0,
pilotXp: 10,
discoveredLocationId: null,