Questline + NPC Progression (The Bonds and the Board)
Status: Part 1 Q1 BUILT — quest scaffold + quest log shipped; Part 2 Q2 BUILT — NPC relationship scores, the five tiers and their one-time payoffs, the NPC SIDE quest chains, and the Arc One ending su
Last updated: September 8, 2026
Dark Tower — Questline + NPC Progression (The Bonds and the Board)
Status: Part 1 Q1 BUILT — quest scaffold + quest log shipped; Part 2 Q2 BUILT — NPC relationship scores, the five tiers and their one-time payoffs, the NPC SIDE quest chains, and the Arc One ending survival branch shipped; Q3 (post-story integration) BUILT — the forward-compatible post-story quest/NPC integration shipped; the literal Ascension carry-over WIRING is deferred to the post-story-loop build PS1-PS4, which is not yet built (see Q3 build status). Originating design: DESIGN (game-design audit gd-02 — process-reports report 3, finding 11. Owner chose "full questline + NPC system buildout, the larger path"). No engine code in this task — this doc is the spec; the build epics are flagged at the end and filed to the queue. Companion to the post-story-loop.md (the activity loop); together they close gd-02.
Parent: index.md. Sibling: post-story-loop.md (the deep activity loop this dovetails with). Reuses: narrative.md (the quest stub, the NPC relationship stub, the clue system, the Arc One ending router 35877), economy.md (the
[NAME]_RELATIONSHIP_IMPROVEreward +[NAME]_RELATIONSHIP_HURTpunishment types, the pending-reward pipeline, the Title Shop, premium-currency audit discipline, permanent-buff caps), combat.md
- progression.md (the
enemyKillscounter, the 6-slot companion system, level/clue milestones), exploration.md (theactiveBountiessystem, deep activities), engine/daily-framework.md (the DELTA-since-snapshot objective pattern). Voice standard: philosophy/player-experience.md.
Thesis (the original problem -- RESOLVED by Q1-Q3 below; kept for rationale)
This section describes the pre-2026-06-17 state that motivated this doc. Q1-Q3 (below) shipped the fix; narrative.md § Quest System now reflects the live system, not this stub. gd-02 named two intertwined gaps in Dark Tower: a stubbed quest system and stubbed NPC payoffs. At the time, narrative.md documented both precisely as:
- Quests —
DARK_TOWER_QUEST_LOOKUProutes to , which only collects auction winnings. There is no quest log, no quest data model, no quest tracking, no quest-card linking, and no quest types. The word "quests" in the handler is aspirational. - NPC relationships — eight named NPCs (William, Isabel, Trinity, Cuthbert, Chelsea, Nigel, Lucas,
Rylee) already have BOTH a
[NAME]_RELATIONSHIP_IMPROVEreward type (economy.md Standard Rewards) and a[NAME]_RELATIONSHIP_HURTpunishment type (economy.md Punishment System). Nothing reads or writes a relationship score, nothing tiers it, nothing pays it off, and nothing feeds it back into the story. The reward/punishment types fire into the void.
This doc designs the system that closes both: a real quest layer that gives the player a structured "what should I do, where am I, what next" anchor, and a real NPC relationship layer where the eight named characters become people the player invests in and gets paid off by — culminating in who survives the ending. The two are one system: NPC arcs ARE quest chains, and quest completion is a primary mover of relationship score. Both are built almost entirely on systems and counters Dark Tower already has (clues, counters, the companion system, the Title Shop, the ending router, the pending-reward pipeline), so they add structure, goals, and payoff — not net-new gameplay telemetry.
How this complements the post-story loop (the hard boundary)
post-story-loop.md (The Ascendant's Path) is the deep activity loop — Ascension (NG+), Hunter's Contracts, Trial of the Week — the repeatable things a finished player DOES. This doc is the goal + relationship layer — the quests that frame those activities and the NPC bonds that give them emotional stakes. They are non-overlapping by construction:
| Concern | Owner | What it is |
|---|---|---|
| The small daily ritual | daily.md (E5, SHIPPED) | One same-day kill/clue bounty |
| The deep repeatable activities | post-story-loop.md | Ascension / Contracts / Trial |
| The goal index + progress anchor | THIS doc, Part 1 (Quests) | The quest log: "what should I do, where am I, what next" across all cadences |
| The relationships + their payoff | THIS doc, Part 2 (NPCs) | Eight named NPCs become tiered bonds with tangible + narrative payoff (who survives) |
The quest log surfaces and references the daily, contracts, and trial — it does NOT re-implement them. It is the single voice-first hub that answers "what should I do?" by listing the active goals from every cadence. The contracts/trial/daily remain their own systems; the quest log is the index over them, plus the home of the MAIN-story, SIDE (NPC + exploration), and ACHIEVEMENT quests this doc adds.
Part 1 — The Quest System (turning the stub into a real layer)
Design principle: quests OBSERVE existing state, they don't re-instrument the game
The single most important decision: a quest is an overlay on the clue/counter/flag state the game already maintains, not a new per-card data structure. Dark Tower has 612,042+ cards; adding quest-reference fields to cards (the gap narrative.md § Quest System lists) would be a massive, brittle content migration. Instead, a quest's objective is a small declarative observer that reads an EXISTING signal:
- a clue in
cluesDiscovered(e.g.CLUE_UNLOCKED_CHAPTER_THIRTEEN, an NPC arc clue), - a counter on (
enemyKillstotal or per-type,cluesDiscoveredsize, level, goddevotionLevels,towerStats, dungeon/wave/raid completion flags), - or an NPC relationship score (Part 2).
Progress for cumulative objectives uses the DELTA-since-snapshot pattern already proven by the daily
framework (DELTA_SINCE_DAY_START) and re-used by Hunter's Contracts (DELTA_SINCE_CONTRACT_START): the
quest snapshots the lifetime counter when the quest becomes ACTIVE, and progress = current − snapshot. No new
per-kill or per-card event plumbing. This is the same trick three shipped systems already use; quests are the
fourth consumer of it.
Quest model
DarkTowerQuest (a definition — static catalog, code-resident)
├── questKey: DarkTowerQuestTypes (new enum)
├── category: MAIN | SIDE | ACHIEVEMENT | POST_STORY
├── title: String (stable, hermetic-assertable, e.g. "William's Trust")
├── spokenDescription: String (voice-first objective text)
├── unlockPredicate: observer (LOCKED → AVAILABLE gate: a clue / level / prior-quest-done)
├── objectives: List<QuestObjective>
│ ├── signal: CLUE | COUNTER_TOTAL | COUNTER_TYPED | RELATIONSHIP | ACTIVITY_FLAG
│ ├── key: String (the clue / counter / enemy type / NPC / flag)
│ ├── target: long (e.g. 1 clue, 100 kills, relationship tier 3)
│ └── mode: ABSOLUTE | DELTA_SINCE_ACTIVE (ABSOLUTE for "have you ever", DELTA for "since accepting")
├── chain: questKey | null (the next quest in this chain, unlocked on CLAIMED)
└── reward: DarkTowerReward (paid through the EXISTING pending-reward pipeline)
(new minimal state)
├── questStates: Map<DarkTowerQuestTypes, QuestState> (LOCKED/AVAILABLE/ACTIVE/COMPLETE/CLAIMED + snapshot)
QuestState carries the per-quest status enum and the DELTA snapshot value captured when the quest went
ACTIVE. That single map is the only new persisted field the quest layer needs; everything an objective reads
already lives on the cache. (The build epic MAY instead persist the quest ledger in a claude.* table,
mirroring the daily's claude.daily_challenge decision — decided at build time per
.claude/rules/data-catalog.md. Default: the on-cache map, smallest blast (repository root)
radius.)
Quest state machine
LOCKED ──unlockPredicate true──► AVAILABLE ──player accepts (or auto for MAIN)──► ACTIVE
ACTIVE ──all objectives met──► COMPLETE ──player claims──► CLAIMED ──chain set──► unlock next quest
- MAIN quests auto-accept (they track the player's actual story position — there is nothing to "accept").
- SIDE / ACHIEVEMENT / POST_STORY quests surface as AVAILABLE and the player accepts them (
accept quest), which captures the DELTA snapshot so "kill 100 cultists" counts from acceptance, not retroactively. (A build-time tuning call may let some ACHIEVEMENT quests use ABSOLUTE/"have you ever" mode so a veteran's lifetime total counts — decided per quest in the catalog.) - COMPLETE → CLAIMED is the reward gate. Claiming an incomplete quest is a graceful no-op that re-states progress (never a dead-end, never an error).
The four quest categories
| Category | What it tracks | Signal source | Example |
|---|---|---|---|
| MAIN | Story-chapter progression — the "where am I / what next" anchor | chapter milestone clues + meta_chapters |
"Reach the Lich's Bargain" (clue CLUE_UNLOCKED_CHAPTER_THIRTEEN) |
| SIDE | NPC arcs (Part 2) + exploration milestones | NPC relationship tier, location/activity clues, devotionLevels |
"William's Trust" (NPC arc); "Pilgrim" (pray at all 8 god points) |
| ACHIEVEMENT | Lifetime mastery milestones | enemyKills totals, level, towerStats, dungeon clears |
"Demon Hunter" (1000 DEMON kills); "Tower Conqueror" (waves round 50) |
| POST_STORY | The Ascendant's Path goals, surfaced as quests | references contracts/trial/ascension state | "Take a Hunt" (→ Hunter's Contracts); "This Week's Trial" (→ Trial of the Week) |
MAIN quests directly answer the "stuck on chapter 12" confusion class
The recurring "I'm stuck / the story is repeating / I can't reach chapter 14" reports
(narrative.md Design Decisions) are partly a navigation problem and
partly a "the player has no in-game sense of their own progress or next goal" problem. A MAIN quest line
("Chapter 13: Reach the Lich's Bargain") read off the existing chapter-unlock clues gives the player an
explicit, voice-first answer to "what chapter am I on and what's next" — complementing the
DARK_TOWER_WHAT_CHAPTER handler with a goal, not just a status. MAIN quests never gate progress (the story
auto-advances regardless); they narrate it.
The quest log (the surface)
A real quests / quest log voice surface, reached by quests, quest log, my quests, active quests,
what should I do, what next. It reads the player's questStates and speaks, voice-first
(philosophy/player-experience.md):
"You have three active quests. William's Trust — earn William's loyalty, you are at tier two of four. Demon Hunter — slay one thousand demons, you are at six hundred forty. This week's Trial is the Lich Lair. Say a quest name to focus it, or say daily for today's Bounty Board."
The quest log is the unified "what should I do" hub: it lists the player's MAIN next-step, accepted SIDE/ ACHIEVEMENT quests, and a one-line reference to each post-story cadence (daily / contract / trial) without re-implementing them.
NLU — repurpose the stub, don't strand the auction collect (mandatory)
DARK_TOWER_QUEST_LOOKUP today routes to , which
calls . That auction-collect behavior must NOT be lost — it
is a real feature players use. The build epic:
- Routes the
quests/quest logfamily to the NEW quest-log readout. - Preserves the auction collect as
collect winnings/collect rewards(and keeps it reachable from the quest-log readout as a "you have winnings to collect" line when the auction service has items). The collect-winnings path stays wired; it just stops masquerading as the entire quest system. - Collision check (mandatory, both directions): verify no shadowing against the daily
daily/bounty boardsurface (daily.md § NLU) and the post-storycontracts/trial of the weeksurfaces (post-story-loop.md Pillars B/C).questphrases must not steal those, and vice-versa — with scenario_tests in both directions, exactly as did fordailyvsbounty board. Note the synonym mappergetSynonymsToCompareTodoes NOT honor theenabledflag (a shadowing synonym must be DELETED, not disabled) — the lesson from the daily shadow fix.
Quest rewards (every grant rides an existing audited path)
Quest completion pays through the existing pending-reward pipeline (pendingRewards →
finalizeRewardsExternal(), economy.md). No new faucet, no new currency:
| Quest tier | Reward | Path | Audit |
|---|---|---|---|
| MAIN milestone | Gold (soft) + occasional gear/item roll | pendingRewards standard rewards |
Soft-currency carve-out, money-cap aware |
| SIDE (NPC arc) | Relationship improve + a tier payoff (Part 2) | [NAME]_RELATIONSHIP_IMPROVE reward type |
Part 2 |
| ACHIEVEMENT | Gold + a Title Shop eligibility unlock (cosmetic) | pendingRewards + Title gate predicate |
Title gold-tax unchanged |
| POST_STORY | references contracts/trial rewards — paid by THOSE systems | — | Owned by post-story-loop.md |
Premium (diamond) quest rewards, where used at all, are milestone-only and ride
with a mandatory game_log.purchase_transactions audit row in the same call
(the discipline economy.md + daily.md enforce). Quests never invent a currency and
never grant raw combat stats.
Part 2 — NPC Relationships + Payoff Arcs (wiring the stubbed types into real bonds)
Design principle: the reward/punishment types already exist — make them MOVE a real score
The eight NPCs already have live reward AND punishment types. The whole gap is that they move nothing. The
fix is literal: a numeric per-NPC relationship score that [NAME]_RELATIONSHIP_IMPROVE raises and
[NAME]_RELATIONSHIP_HURT lowers, with thresholds that fire payoffs. The design intent
narrative.md already records — "personal investment in specific characters rather than an
abstract reputation meter" — is honored: eight individual scores, eight individual arcs, not one global
reputation bar.
The eight NPCs and their roles
| NPC | Existing role (narrative.md) | Arc theme (build-epic authors the beats) |
|---|---|---|
| William | Primary ally; dies in the Pyrrhic Victory ending | Loyalty / brotherhood — the headline survival payoff |
| Isabel | Secondary companion | Trust earned through the orc-alliance arc (existing cards 30605–30647) |
| Trinity | Secondary character | — |
| Cuthbert | Supporting | — |
| Chelsea | Supporting | — |
| Nigel | Supporting | — |
| Lucas | Supporting | — |
| Rylee | Supporting | — |
(The arc beats — the spoken NPC lines and the side-quest cards — are narrative authoring delivered by the build epic + the storyline writers; this doc specifies the SYSTEM, not the prose.)
Relationship score + tiers
New state:
npcRelationships: Map<DarkTowerNpc, Integer>on (newDarkTowerNpcenum of the eight names). Absent key = 0 (Stranger). One small map — the minimal new field.The score is moved ONLY by the existing reward/punishment types (and, downstream, by completing an NPC's SIDE quest chain, which grants the improve reward):
Mover Effect [NAME]_RELATIONSHIP_IMPROVEreward+Nto that NPC's score (N tunable per grant; default small)[NAME]_RELATIONSHIP_HURTpunishment−Nto that NPC's score, floored at the Stranger tier (never below 0)Completing an NPC SIDE quest chain step grants the improve reward (the quest layer's main relationship driver) Tiers (thresholds tunable in the build; named here for the voice copy + payoff gates):
Tier Name Payoff fired on FIRST crossing 0 Stranger — 1 Acquaintance A spoken acknowledgement beat; the NPC's SIDE arc quest becomes AVAILABLE 2 Ally A tangible non-combat payoff (see below) 3 Confidant The NPC becomes a recruitable companion (reuses the 6-slot companion system) 4 Sworn The narrative payoff: the NPC's fate in the ending improves (see "Who survives") + a unique Title Each tier crossing is idempotent and one-time (mirror
hasClue/ensureRaidBossClueUnlocked— exploration.md): a per-NPC "highest tier paid" cursor guards the payoff so a score that oscillates across a threshold pays the tier exactly once.
What the tier payoffs ARE (economy walls respected — NEVER raw combat stats)
The relationship payoffs obey the same wall the post-story loop does: the combat-stat lane is paragon-only (economy.md § Permanent Non-Combat Buffs). NPC payoffs are prestige, narrative, utility, and companionship — never a flat damage/HP/AC number:
- Spoken bond beats (Acquaintance) — the NPC speaks to the player by name, acknowledging the growing relationship. Pure narrative payoff, zero economy cost.
- Tangible non-combat boon (Ally) — one of: a one-time gold/item reward via the pending-reward pipeline, a unique clue that unlocks side content / a fast-travel node, or a QoL tick that shares the existing permanent-buff caps so it can never exceed them (economy.md § Permanent Non-Combat Buffs).
- Companion unlock (Confidant) — the NPC becomes recruitable into one of the existing 6 companion slots (index.md Character System; progression.md). The companion's combat contribution comes entirely through the existing companion-bonus system (no new stat path) — so a beloved NPC literally fights at your side, using machinery that already exists. This is the strongest "I invested in this character and got them back" payoff and it costs no new combat math.
- Ending survival + unique Title (Sworn) — see below + a "Sworn of {NPC}" / "{NPC}'s Champion" entry in the existing Title Shop rotation (economy.md § Title Shop), gated by the Sworn tier, bought with gold like every other title (tier = eligibility, gold = tax). Reuses the title gate + voice plumbing wholesale.
The headline payoff — "Who survives the ending" (relationship as a SECOND input to the existing router)
The Arc One ending router (card 35877) is today a pure function of clue count (narrative.md: 12+ Total, 8–11 Pyrrhic [William dies], 4–7 Tragic [multiple deaths], 0–3 Defeat). The NPC system adds a second input without rewriting the router's clue-count spine:
- William's survival in a Pyrrhic ending. A player at the Pyrrhic clue band (8–11) whose William relationship is Sworn (tier 4) sees William survive — the ending narration branches on the relationship flag. The clue count still sets the macro ending; the relationship decides who you saved within it. This is the concrete, emotionally-loaded replay/engagement goal: "I got the Pyrrhic ending and William died — this run I'll earn his trust and save him." It reuses the existing ending router (one new branch read off a flag), not a new ending system.
- Generalize across the named NPCs (build-epic, with the storyline writers): each "death" in the Tragic band (4–7 clues, "multiple character deaths") is gated on that NPC's relationship — a Sworn NPC survives even a Tragic ending. The macro ending stays clue-gated; the survivor list is the relationship payoff.
- Track a per-storyline best-ending + survivors high-water mark so the player sees their own target (pairs with the post-story-loop "better ending" pull — post-story-loop.md).
NPC arcs ARE quest chains (the unification)
Each NPC's relationship is climbed through a SIDE quest chain (Part 1): e.g. William's Trust →
William's Burden → William's Oath, each step unlocked by the prior (chain), each completion granting
WILLIAM_RELATIONSHIP_IMPROVE and advancing the tier. So the two halves of gd-02 are one mechanism:
- The quest log shows the player which NPC arcs are open and how far along each is.
- Completing the arc quests is the primary, player-driven way to raise relationship (alongside the in-story reward/punishment grants that already fire from card content).
- Reaching Sworn delivers the survival + title payoff.
A player always has a legible answer to "how do I save William?": open his quest chain in the quest log and complete it.
Post-story continuation (the NPC half of the activity loop)
After the main story, NPC arcs do not evaporate — this is the NPC contribution to the "post-story activity loop" the task asks for, complementing post-story-loop.md:
- Relationships carry across Ascension. Ascension (NG+) preserves the character intact
(post-story-loop.md Pillar A carry-over) —
npcRelationships, recruited companions, and Sworn titles persist. A bond earned once is permanent across every re-run; the NPC who survived stays at your side. - The quest log spans the cadences. Post-story, the quest log's POST_STORY category surfaces the active Hunter's Contract and the week's Trial as quests, so the same "what should I do" hub keeps working when the authored story is exhausted.
- High-relationship NPCs frame post-story activity (build-epic, light): a Sworn NPC can deliver the Ascendant's Path hub readout in their voice / offer a contract hand-in beat — pure narrative dressing over the existing contract/trial systems, no new mechanics.
This gives a max-progressed player both a reason to keep playing (the Ascendant's Path activities) AND someone to keep playing with/for (the persistent NPC bonds) — the retention pairing gd-02 was missing.
Reward-economy safety (summary)
Per economy.md and the daily/post-story precedent, every grant rides an existing audited path:
| Grant | Currency | Path | Audit |
|---|---|---|---|
| MAIN/ACHIEVEMENT quest gold | Gold (soft) | pendingRewards / |
Soft-currency carve-out, money-cap aware |
| Quest gear/item roll | item | pendingRewards standard reward |
Existing loot pipeline |
| NPC Ally tangible boon | gold / item / capped QoL | pendingRewards (shares permanent-buff caps for QoL) |
Never exceeds existing caps |
| NPC Confidant companion | — (companion slot) | existing 6-slot companion + companion-bonus system | No new stat path |
| NPC Sworn title | Gold (the tax) | existing Title Shop gate + catalog row | Title gold-tax unchanged |
| Any milestone diamonds | Diamonds (premium) | (existing/POST_STORY source) |
Mandatory purchase_transactions row |
| Ending survival branch | — (narrative flag) | existing 35877 ending router + one relationship-flag read | No currency write |
No grant invents a currency or hands out raw combat stats. The combat-stat lane stays paragon-only.
Voice-first / never-dead-end (mandatory)
Every quest-log and NPC surface obeys philosophy/player-experience.md: state what happened, where they are, and a clear next action, with no parens / brackets / slashes / abbreviations. Canonical shapes (starting copy, tune in build):
- Quest log: "You have two active quests. William's Trust, earn his loyalty, you are at tier two of four. Demon Hunter, slay one thousand demons, you are at six hundred forty. Say a quest name to focus it, or say what next for your story goal."
- NPC tier-up beat: "William claps you on the shoulder. You have earned his trust. He will fight at your side now. Say companions to bring him along."
- Ending survival payoff (Pyrrhic, William Sworn): "The citadel falls, but William stands beside you, breathing. The bond you forged held when it mattered."
- Claim-an-incomplete-quest no-op: "That quest is not finished yet. Slay one thousand demons, you are at six hundred forty. Come back when it is done."
Regression shape (hermetic — for the build epics)
Per .claude/rules/hermetic-scenario-tests.md — every test seeds
its own precondition and asserts the date/RNG/order-independent surface only. Author scenario_tests via
the canonical MCP tool add_scenario_test (raw INSERT prohibited); pure service/objective math goes in JUnit.
- Quest progression → JUnit + scenario_test: seed a known counter snapshot, advance the underlying counter
(e.g.
enemyKills), assert the quest reads the correct "N of M" DELTA progress and transitions ACTIVE → COMPLETE.claimon an incomplete quest is a graceful no-op that re-states progress. Assert the stable quest key/title + progress shape, NEVER a rotating objective's prose. (Quest progression scenario_test.) - NPC payoff → JUnit + scenario_test: seed an NPC relationship score one point below the Ally (or
Confidant) threshold, apply a
[NAME]_RELATIONSHIP_IMPROVEgrant, assert the tier crosses, the payoff fires exactly once (companion-unlock flag set / title eligibility set / survival flag set), and a second identical grant does NOT re-pay (idempotent cursor). For the survival payoff: seed a Pyrrhic clue count + William at Sworn, assert the ending branch reports William alive; seed Pyrrhic + William below Sworn, assert he dies (the existing behavior, now a control). (NPC-payoff scenario_test.) - Post-story loop tie-in → scenario_test: seed a completed-story character (
CLUE_PATH_OF_ASCENSION_OPENvia the existingsetStoryCluesseeder, narrative.md / post-story-loop.md), assert the quest log's POST_STORY category surfaces the contracts/trial reference and that an NPC arc still reads its tier. Structural assertion only — never the rotating trial activity's text. (Post-story-loop scenario_test.) - NLU → process_tests: every new phrase (
quests,quest log,my quests,what next,accept quest,collect winnings, NPC-focus phrases), PLUS explicit anti-shadow assertions that the dailydaily/bounty boardand the post-storycontracts/trial of the weekstill resolve to their own surfaces (the task-22369 lesson, both directions).
What this does NOT do (scope guards)
- Does not re-spec the daily (daily.md) or the post-story activity loop (post-story-loop.md). The quest log references them; it never re-implements selection, rewards, or rotation.
- Does not add quest-reference fields to the 612K cards. Quests observe existing clue/counter/flag state.
- Does not grant raw combat stats (paragon's lane). NPC payoffs are narrative, prestige, companionship, and capped QoL.
- Does not rewrite the Arc One ending router's clue-count spine. It adds ONE relationship-flag branch read for who-survives; the clue count still sets the macro ending.
- Does not write new authored story prose here — the NPC arc beats + side-quest cards are narrative authoring delivered by the build epics + storyline writers. This doc is the SYSTEM spec.
- Does not add a cron/scheduler. Quest progress is computed on read from existing counters (the daily/ contract/trial deterministic model).
Build epics (flagged + filed — NO engine code in this design task)
This task is design-only; the Java build is filed to the queue as a sequenced chain (each game-engine epic
carries the per-commit game_test.scenario_tests obligation
.claude/rules/deploy-gating.md and a same-change update to this doc): (repository root)
| # | Epic | Scope | Depends on |
|---|---|---|---|
| Q1 | Quest system scaffold + quest log — BUILT | DarkTowerQuestTypes enum + quest catalog; the QuestObjective observer model (CLUE / COUNTER / RELATIONSHIP / FLAG signals, ABSOLUTE + DELTA_SINCE_ACTIVE modes reusing the daily snapshot trick); questStates state + state machine; the quests / quest log / what next NLU surface repurposing DARK_TOWER_QUEST_LOOKUP while preserving the auction collect winnings path; MAIN-quest auto-tracking of chapter milestones; the anti-shadow NLU guard vs daily/contracts/trial. Scenario_tests: quest progress DELTA read, claim no-op, MAIN chapter track, anti-shadow NLU. |
post-story PS1 scaffold (shares PremiumCurrencySource/hub) — coordinate, not blocking |
| Q2 | NPC relationship + payoff arcs | DarkTowerNpc enum + npcRelationships map; wire [NAME]_RELATIONSHIP_IMPROVE / [NAME]_RELATIONSHIP_HURT into score mutation (floored at Stranger); the 5 tiers + one-time idempotent tier payoffs (spoken beat, Ally boon, Confidant companion unlock via the existing 6-slot system, Sworn title + survival flag); the NPC SIDE quest chains on top of Q1; the ending-router second input (35877 who-survives branch on the relationship flag, William first). Scenario_tests: tier-cross pays once + idempotent, companion-unlock flag, Pyrrhic+Sworn→survive vs below-Sworn→die. |
Q1 |
| Q3 | Post-story quest/NPC integration — BUILT (forward-compatible portion; Ascension-command carry-over deferred to PS2) | The quest log's POST_STORY category surfacing Hunter's Contracts + Trial of the Week as referenced quests; NPC arcs continue post-story; Ascension carry-over of npcRelationships + recruited companions + Sworn titles; Sworn-NPC framing of the Ascendant's Path hub (light narrative dressing). Scenario_tests: post-story quest-log surfaces the cadences, relationship survives an Ascension. |
Q1, Q2, and post-story-loop PS1–PS4 |
Sequence: Q1 → Q2 → Q3 (Q3 also waits on the post-story-loop build PS1–PS4). Each epic updates this doc's status table for its part in the same change.
Q1 build status
Part 1's quest scaffold + quest log shipped as game-engine Java (build green; JUnit ; hermetic scenario_tests 2206-2210). What landed and the deliberate Part-1 scoping:
- Model (
model/darktower/quest/):DarkTowerQuestTypes,DarkTowerQuestCategory,DarkTowerQuestStatus,QuestSignal(CLUE / COUNTER_TOTAL / COUNTER_TYPED / RELATIONSHIP / ACTIVITY_FLAG),QuestObjectiveMode(ABSOLUTE / DELTA_SINCE_ACTIVE), andQuestState(the one new persisted field, on ). - Logic (
engines/handlers/darktower/quest/):QuestObjective,DarkTowerQuest,DarkTowerQuestCatalog(MAIN chapter chain 2 to 13 off the existingCLUE_UNLOCKED_CHAPTER_*clues, ACHIEVEMENT kill/clue milestones, a SIDE devotion quest plus a DELTA "Reckoning", a POST_STORY signpost), (the LOCKED to CLAIMED state machine, the observer progress math, and claim), and (the voice-first readout). - NLU repurpose without a risky DB repoint: the quest log is reached through the EXISTING
DARK_TOWER_QUEST_LOOKUPaction, branched in-code by firing action inside . "quests" / "my quests" reach the quest log; "collect" / "collect rewards" gather auction winnings AND claim finished quests, so the auction collect-winnings path stays reachable. Noprocess_action_handlersrow was repointed (avoids the row-precedes-jar INTERNAL_ERROR trap), so the surface is hermetic on the existing routing.
Deliberate Part-1 simplifications (revisited in Q2/Q3):
- No "accept quest" command yet — SIDE/ACHIEVEMENT quests auto-accept (the AVAILABLE pause is a no-op in Part 1). The explicit accept and the AVAILABLE surface land with the quest-accept NLU bundle.
- RELATIONSHIP signal is modeled but reads zero until Q2 wires
npcRelationships; no relationship quest is in the Q1 catalog, so nothing stalls. - POST_STORY unlock is proxied on
CLUE_UNLOCKED_CHAPTER_THIRTEEN; Q3 repoints it to the real story-complete clue when the post-story loop ships. - Claim rides the "collect" action rather than a dedicated "claim quest" command — keeps the surface hermetic with no new prod NLU row. MAIN milestones and achievements pay soft gold via on collect.
Follow-on prod NLU bundle (safe, additive, post-deploy — each phrase routes to an already-handled
action, so there is no row-precedes-jar risk): add process_one_shot_values rows in DARK_TOWER
context for quest log, active quests, what next, what should i do to DARK_TOWER_QUEST_LOOKUP,
and collect winnings to COLLECT. The phrases quest / quests / my quests / collect rewards
already route today.
Q2 build status
Part 2's NPC relationship + payoff system shipped as game-engine Java (build green; JUnit , ; hermetic scenario_tests 2218-2221). What landed:
- Model (
model/darktower/npc/):DarkTowerNpc(the eight named NPCs, with afromRelationshipConstantresolver that maps a[NAME]_RELATIONSHIP_IMPROVE/_HURTenum to its NPC) andDarkTowerNpcTier(the five tiers with their tunable score thresholds — Stranger 0, Acquaintance 10, Ally 25, Confidant 50, Sworn 100). Two new persisted cache maps:npcRelationships(the live score) andnpcTierPaid(the permanent, never-decreasing paid-tier cursor that backs idempotency, the Sworn survival flag, and Title eligibility). - Logic (
utils/darktower/details/):improve/hurtmove the score (floored at Stranger),reconcileTierPayoffspays each newly-crossed tier exactly once (spoken Acquaintance beat; Ally soft-gold boon via the money path; Confidant companion unlock by granting the companion's own gating clue so it becomes recruitable in the existing 6-slot upgrade picker; Sworn beat — the Title and survival flag both derive from the paid cursor, so no extra write). - Wiring: handles the
*_RELATIONSHIP_IMPROVEreward as an immediate effect (like the recovery rewards, never queued); applies the*_RELATIONSHIP_HURTdecrement (and is now null-row hardened). No raw combat stat is ever granted — payoffs are narrative, prestige, companionship, and capped QoL. - Title Shop: eight
TITLE_SWORN_OF_<NPC>entries added toDarkTowerTitleTypes+ the catalog, each gated byisSwornon that NPC and bought with gold like every other title (tier = eligibility, gold = tax). - NPC SIDE arcs (
DarkTowerQuestCatalog): William's three-step chain (Trust -> Burden -> Oath, Acquaintance to Sworn) and Isabel's two-step orc-alliance chain. Each step observes a kill-DELTA so it is completable through normal play, and grants the NPC improve on claim (advancing the tier and firing its payoff).DarkTowerQuestgainedunlockNpc/unlockNpcTier/npcReward/npcRewardAmount; the questRELATIONSHIPsignal now reads the live tier; the quest log surfaces earned bonds and reconciles tier payoffs on read as a safety net. The remaining six NPCs onboard the same way as the storyline writers author their beats — no core change needed. - Ending router ( + ): on card 35877 the clue-gated option still picks the macro ending (Total/Pyrrhic/Tragic/Defeat, untouched); the new branch reads the relationship flag and emits a survivor epilogue for any Sworn NPC who would die in the Pyrrhic (William) or Tragic (William, Isabel, Trinity) band. A per-storyline best-ending high-water mark is recorded. Below Sworn, the existing authored death outcome stands.
- Test seeder:
setNpcRelationships(PlayRequest + ) seeds a score and reconciles the paid cursor exactly as a live grant would; the bond maps are wiped per scenario run in for hermeticity.
Deliberate Part-2 scoping (storyline-writer / Q3 follow-on):
- NPC arc prose + side-quest cards are authored later by the storyline writers; the Part-2 spokenDescriptions are starter copy and the arc objectives observe the existing kill counter rather than authored arc clues. William's and Isabel's arcs are wired; the other six onboard with no core change.
- The
bondsNLU phrase is deferred as a safe additiveprocess_one_shot_valuesrow (like the Q1 follow-on bundle). NPC bonds are reachable today through the existingDARK_TOWER_QUEST_LOOKUPquest-log surface, so no risky prod NLU repoint ships with Q2. - Ascension carry-over of
npcRelationships/ companions / Sworn titles is Q3 (waits on the post-story loop).
Q3 build status
Part 2's post-story-continuation section shipped the forward-compatible Q3 integration that does NOT require the post-story loop's Java to exist yet (PS1-PS4 — Ascension, Hunter's Contracts, Trial of the Week — are still DESIGN, unbuilt at the time of this change). What landed as game-engine Java (build green; hermetic scenario_tests 2227-2228):
- POST_STORY quest log surfaces both cadences.
DarkTowerQuestCatalog's single POST_STORY signpost (DT_POST_STORY_ASCENDANT) now names BOTH Hunter's Contracts and the weekly Trial as descriptive forward references. It is reference-only and deliberately NOT an actionable command — thecontracts/trialsurfaces ship with PS3/PS4, so the readout never tells the player to say a phrase that does not route yet (no dead-end). Unlock stays proxied onCLUE_UNLOCKED_CHAPTER_THIRTEENuntil PS1 grants the realCLUE_PATH_OF_ASCENSION_OPEN. - Sworn-NPC framing of the hub readout. now
prefixes the post-story line with a one-line beat in the voice of the first Sworn NPC the player has
earned (
swornFraming), reading the permanent paid-tier cursor (isSworn). Pure narrative dressing over the existing reference — the "high-relationship NPCs frame post-story activity" line from post-story-loop.md — with no new mechanic or surface. - NPC bonds continue post-story. The eight bonds live on character-global cache maps
(
npcRelationships,npcTierPaid) that are not story-gated, so they persist and read in the quest log after the story is finished — verified by scenario_test 2227 (a Sworn bond still reads alongside the post-story cadences). - Companion carry-over guarantee (forward-defense for Ascension). A Confidant-tier companion is
recruitable via a granted clue; Ascension (PS2) will CLEAR the storyline's clues on replay, which
would otherwise drop a recruited companion.
now RE-ASSERTS the companion's gating clue from the permanent
npcTierPaidcursor on every reconcile (idempotentaddClue), so a bond once earned keeps its companion across ANY clue reset. This makes the "recruited companions persist across Ascension" guarantee real and derived from cursor state the reset never touches.
Deferred to the post-story-loop build (the hard PS1-PS4 dependency)
Q3's remaining items cannot be wired until the post-story loop exists in code (this task's guidance:
"coordinate so this lands after Ascension/Contracts/Trial exist"). As of this change none of PS1-PS4 is
built — there is no ascend command, no ascensionLevel, no CLUE_PATH_OF_ASCENSION_OPEN, no
Contracts/Trial system, no PremiumCurrencySource.ASCENSION. The carry-over GUARANTEE is in place and
proven via the path Ascension reuses, but the literal Ascension wiring is PS2's:
- PS1 repoints the POST_STORY unlock from the
CLUE_UNLOCKED_CHAPTER_THIRTEENproxy to the real story-complete clueCLUE_PATH_OF_ASCENSION_OPEN, and replaces the descriptive cadence copy with livecontracts/trialreferences once those surfaces route. - PS2 (Ascension) MUST honor the carry-over contract when it clears the active storyline's clues:
npcRelationships,npcTierPaid(the Sworn title eligibility + the ending-survival flag), andownedTitlesare character-global cache fields the existing story-switch carry-over (, the path PS2 reuses) already preserves untouched; the recruited-companion clue is the one fragile point, and PS2's clue-clear MUST be followed byreconcileTierPayoffs(which now re-asserts the companion clue from the cursor) so no companion is lost. The reassert added here IS that contract, enforced now as a self-healing safety net.
Related docs
- post-story-loop.md — the deep activity loop this pairs with (Ascension / Contracts / Trial); together they close gd-02.
- narrative.md — the quest stub + NPC-relationship stub this replaces; the Arc One ending
router (35877), clue system,
setStoryCluesseeder. - economy.md — the
[NAME]_RELATIONSHIP_IMPROVE/_HURTtypes, pending-reward pipeline, Title Shop, permanent-buff caps, premium-audit discipline. - index.md — the 6-slot companion system, the clue/ending overview, character state.
- progression.md — companions, level/devotion milestones (quest + tier gates).
- combat.md — the
enemyKillscounter quests + achievements read. - exploration.md — the
activeBountiessystem + deep activities the POST_STORY quests reference. - daily.md + engine/daily-framework.md — the DELTA-snapshot
objective pattern + the
daily/bounty boardsurface the quest log references and must not shadow. - philosophy/player-experience.md — voice-first, never-dead-end.
.claude/rules/hermetic-scenario-tests.md/.claude/rules/deploy-gating.md— the regression discipline for (repository root) the build epics.