What changed, and why
Everwhen resolves each turn with two levers. The roll decides which band you land in (Critical Failure up to Critical Success); craft adds a small ±1/±2 tilt to the d20 — a nudge to your luck. The swing decides how many percent the meter actually moves inside that band, chosen by the Timeline Engine after the band is set. Writing tilts the dice; aim drives the size.
The Message Judge (Layer 0) grades a dispatch's craft into the tilt. It was objective-aware: it received the player's grand objective and graded a LEVERAGE axis — "does this figure hold the power to move the world toward the objective." Two problems followed. A mechanically load-bearing rater knew the goal and was asked to score goal-fit, so it could quietly tilt fate toward on-goal play regardless of how the message was written. And it double-counted: the Timeline Engine already scores whether the action moved the goal, through progressChange. Goal-alignment hit the outcome twice.
This PR makes the Judge objective-blind. It drops the objective from the Judge's inputs and removes the LEVERAGE axis. The Judge now grades pure writing craft — sharpness and period-fit, plus continuity and timing when the run gives it those — none of which need the goal. The net model: how well you wrote sets the tilt; how well you aimed sets the swing, via the Timeline Engine, which already owns goal-fit. Thirteen files, +42/−40; the load-bearing edits are in three.
The Judge prompt: the objective and LEVERAGE come out
buildJudgePrompt is where the bias lived. The objective was a required parameter, printed as a GRAND OBJECTIVE line, and the LEVERAGE axis asked the model to grade movement "toward the objective." All three are gone. The args type no longer accepts an objective; the prompt no longer prints one; the axis list drops to sharpness and period-fit, and a new line tells the model outright that it does not know the player's aim and must not reward one.
The objective-blind doc + signature (no objective param), and the axis list.
server/utils/prompt-builder.ts · 531 lines
⋯ 208 lines hidden (lines 1–208)
⋯ 16 lines hidden (lines 222–237)
⋯ 284 lines hidden (lines 248–531)
The seam: callJudgeAI stops taking an objective
callJudgeAI is the transport around the prompt. Its args type drops the objective field, and the doc comment flips from "objective-aware (it judges leverage toward the goal)" to objective-blind. Nothing else in the function moves: it still passes args straight to buildJudgePrompt, and the response schema — the event/timing pair for a pinned moment, craft, the optional continuity, and reason — is untouched, because none of those fields ever depended on the goal.
The doc comment and the args type — figureName leads now, no objective.
server/utils/openai.ts · 578 lines
⋯ 277 lines hidden (lines 1–277)
⋯ 276 lines hidden (lines 303–578)
The call site, and the double-count fix
In the turn handler, Step 1 builds the Judge call. It no longer passes objectiveContext. The contrast is one screen down: Step 4's Timeline Engine call still receives the objective, because goal-fit is its job. That side-by-side is the whole architecture of the change — the same objectiveContext value, fed to the swing but not the tilt.
Step 1 (Judge) drops the objective; Step 4 (Timeline) keeps it.
server/api/send-message.post.ts · 373 lines
⋯ 194 lines hidden (lines 1–194)
⋯ 54 lines hidden (lines 207–260)
⋯ 108 lines hidden (lines 266–373)
What didn't change: the EV corridor
The tilt's meaning shifts — pure craft now, not craft-plus-aim — so the natural question is whether the difficulty curve moves. It doesn't, structurally. No band-table or modifier math is touched: SWING_BANDS, the craft-to-modifier map, and applyCraftModifier are all untouched, and craft still feeds both the tilt and the gains multiplier. What shifts is only which dispatches earn which grade, not what a grade is worth. The tuning-intent comment that pins the corridor still reads true.
The run-economy tuning intent — unchanged, and still accurate.
utils/swing-bands.ts · 71 lines
⋯ 10 lines hidden (lines 1–10)
⋯ 55 lines hidden (lines 17–71)
The behavior is locked by a test. The rewritten Judge prompt test asserts the positive (figure, message, moment all present) and the new negative: the objective title, the GRAND OBJECTIVE header, and the LEVERAGE axis are absent from the prompt. If any of them leak back in, this fails.
Objective-blindness, asserted both ways.