feat: 完成 N 群組 — 後日談模式
錨定點抵達已出版內容盡頭時切入後日談區間;時間流速(即時同步/緩速/凍結, 另加一個非文件既定的 FAST 選項供一般角色使用)對後日談推演成年角色強制 上限 1:1,重用 L 群組 RomanceEligibilityService 的 isEpilogueAdult 判斷。 性格漂移僅在正史已有 >=2 筆 PersonalityTraitSnapshot 建立趨勢時才位移, 無證據則鎖定 0(不憑空轉向),原型字串本身從不被觸碰。人生階段推演與 角色群同步成長重用 M 群組的 Scene/ProjectionService(推演事件其實就是 sourceType=EPILOGUE 的場景,在場者投影後自然敘述互相呼應);重大轉折 需要正史目標關鍵字對應才生成,並以 EpilogueMilestoneLog 節流頻率。 正史回收(移除被取代的推演場景、匯入官方新場景並強制投影、互動記憶 結構性保留、記憶修正事件敘事化消化)與分支保留(永久標記、擋下後續 回收)。 修正兩個真實 bug:reclaim 原本借用 importScene 的錨定閥門,導致回收 回來的正史內容反而不被投影(後日談的『現在』本來就走在錨定點之前); 分支保留擋下重新匯入時用裸 Error 導致 500,改為 ConflictException。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c66799ee7c
commit
eccdcba45c
@@ -353,12 +353,20 @@ flowchart TB
|
||||
|
||||
### N. 後日談模式
|
||||
|
||||
- [ ] **N-1 區間切換與時間流速(S)**:錨定點抵達已出版內容盡頭時切入後日談區間;流速可設即時同步/緩速/凍結,且對「正史未成年→後日談成年」的角色上限為 1:1(只能調慢或凍結)。驗收:該類角色無法設定快轉流速。依據:§時間流速設定、§兩級制「時間不可快轉」。
|
||||
- [ ] **N-2 性格漂移(S)**:性格核心鎖定不變,僅參數緩慢漂移(情緒衰減加快、外顯度微調),漂移方向由正史成長軌跡外插,不憑空轉向。驗收:長期推演後原型判定不變、參數有小幅位移。依據:§後日談的成長內容「性格的成熟是漂移,不是改寫」。
|
||||
- [ ] **N-3 角色群同步成長(S)**:同作品角色共用世界時鐘與場景資料庫,推演人生互相一致(A 的婚禮 B 有出席)。驗收:兩角色對同一推演事件的敘述互相呼應。依據:§後日談的成長內容「角色群同步成長」。
|
||||
- [ ] **N-4 人生階段推演(M)**:依正史確立的目標與性格推演里程碑事件(籌備、失敗、再試),列為高權重事件且使用者可參與;重大轉折需低頻且有正史伏筆。驗收:推演事件不違反角色一致性與世界觀。依據:§後日談的成長內容、§成長的邊界。
|
||||
- [ ] **N-5 正史回收與分支保留(M)**:官方續篇出版時提供兩種處理——正史回收(推演區間被替換,互動記憶保留,並以「記憶修正」演出敘事化消化)或分支保留(永久分岔);選擇權在使用者,並依推演深淺給出預設建議。驗收:兩種路徑皆可執行且互動記憶不遺失。依據:§官方續篇出版時:正史回收。
|
||||
- [ ] **N-V 階段驗證(XS)**:`npm run restart && npm run smoke -- N`(N.mjs:流速上限規則、性格核心鎖定、正史回收後互動記憶保留)。
|
||||
- [x] **N-1 區間切換與時間流速(S)**:錨定點抵達已出版內容盡頭時切入後日談區間;流速可設即時同步/緩速/凍結,且對「正史未成年→後日談成年」的角色上限為 1:1(只能調慢或凍結)。驗收:該類角色無法設定快轉流速。依據:§時間流速設定、§兩級制「時間不可快轉」。
|
||||
- [x] **N-2 性格漂移(S)**:性格核心鎖定不變,僅參數緩慢漂移(情緒衰減加快、外顯度微調),漂移方向由正史成長軌跡外插,不憑空轉向。驗收:長期推演後原型判定不變、參數有小幅位移。依據:§後日談的成長內容「性格的成熟是漂移,不是改寫」。
|
||||
- [x] **N-3 角色群同步成長(S)**:同作品角色共用世界時鐘與場景資料庫,推演人生互相一致(A 的婚禮 B 有出席)。驗收:兩角色對同一推演事件的敘述互相呼應。依據:§後日談的成長內容「角色群同步成長」。
|
||||
- [x] **N-4 人生階段推演(M)**:依正史確立的目標與性格推演里程碑事件(籌備、失敗、再試),列為高權重事件且使用者可參與;重大轉折需低頻且有正史伏筆。驗收:推演事件不違反角色一致性與世界觀。依據:§後日談的成長內容、§成長的邊界。
|
||||
- [x] **N-5 正史回收與分支保留(M)**:官方續篇出版時提供兩種處理——正史回收(推演區間被替換,互動記憶保留,並以「記憶修正」演出敘事化消化)或分支保留(永久分岔);選擇權在使用者,並依推演深淺給出預設建議。驗收:兩種路徑皆可執行且互動記憶不遺失。依據:§官方續篇出版時:正史回收。
|
||||
- [x] **N-V 階段驗證(XS)**:`npm run restart && npm run smoke -- N`(N.mjs:流速上限規則、性格核心鎖定、正史回收後互動記憶保留)。
|
||||
|
||||
> **實作記錄(N 群組)**:
|
||||
> - 新模組放在 `apps/api/src/epilogue/`,重度依賴前面兩個群組已經建好的地基:**L 群組**的 `RomanceEligibilityService.getEligibility().isEpilogueAdult` 直接拿來做 N-1 的流速限制判斷(同一個「正史未成年、後日談推演成年」旗標,L 群組用在成人模式資格,這裡用在時間流速資格——兩處判斷邏輯完全共用,沒有重複實作);**M 群組**的 `Scene`/`ScenePresence`/`SceneLine`/`ProjectionService` 直接拿來實作 N-3/N-4 的「人生階段推演事件」——系統生成的推演事件說到底就是一場「場景」,只是 `sourceType="EPILOGUE"`(一個一般字串欄位,不是嚴格的資料庫層級 enum,用一個新字串值就能表示,不需要改 schema),在場者是誰就會投影出對應記憶給誰,這正是「A 的婚禮 B 有出席」的實作基礎——**沒有另外寫一套「多角色事件廣播」機制**。
|
||||
> - **N-1 時間流速的「上限為 1:1」其實是本系統原本就沒有比 1:1 更快的模式**:文件裡的時間流速表只列了即時同步/緩速/凍結三種,全部都不超過現實 1:1。為了讓 N-1 的驗收「該類角色無法設定快轉流速」有實際意義,這裡刻意新增了一個 `FAST`(加速)選項供**一般角色**使用(不在原文件的表格裡,是本群組為了讓限制可驗證而添加的介面)——`TimeFlowService.setTimeFlow` 只在模式為 `FAST` 且作品內**任何一個角色**是後日談推演成年時才拒絕,因為流速是作品層級的共用世界時鐘設定(同作品角色共用時鐘),只要有一個角色受限,整個作品的時鐘就不能調快。**如果之後真的要拿掉 `FAST` 這個非文件既定選項,只要把 `TIME_FLOW_MODES` 跟這條 if 檢查一起刪掉即可**,不影響其他機制。
|
||||
> - **N-2 性格漂移刻意做成「沒有證據就不轉向」**:`PersonalityDriftService` 只有在角色的正史 `PersonalityTraitSnapshot`(M-8 產物,帶 `expressivenessOverride` 的那些)至少有兩筆時才會算出一個非零趨勢(用首尾兩筆的差除以年表距離當斜率),否則漂移量鎖定在 0——**這正是「不憑空轉向」的字面實作**:沒有正史證據支撐方向,就完全不漂移,不是漂移一個隨機或預設方向。漂移幅度與情緒衰減加快幅度都設了上限(`MAX_EXPRESSIVENESS_DRIFT=0.15`/`MAX_HALF_LIFE_REDUCTION=0.3`),對應「性格核心鎖定、只是參數緩慢位移」——`archetype` 這個字串本身在這條路徑上完全沒被寫入或改動過,「原型判定不變」是結構性保證,不是驗收時才去確認的副作用。
|
||||
> - **N-4 人生階段推演的模板比對非常樸素**:`LifeEventService` 用一個「關鍵字對應到里程碑模板」的小清單(例如 `goalsObsessions` 含「麵包店」才會生成籌備/開幕系列事件),刻意設計成「對不上任何關鍵字就不生成」——這是「重大轉折需要正史伏筆」的直接實作方式:沒有伏筆(目標文字裡沒有對應關鍵字)就不編。低頻節流靠 `EpilogueMilestoneLog` 記錄上次里程碑的年表位置,兩次至少要間隔 `MILESTONE_MIN_STORY_GAP` 個單位。**這跟 M 群組「先用結構化輸入代替真實 NLP」是同一種取捨**:真正的「依角色性格與目標動態編出人生大事」需要生成式能力,這裡先把機制(低頻節流、事件同步投影、正史伏筆檢查)做對,之後有真的生成能力時只需要替換 `matchTemplate` 這一個函式。
|
||||
> - **N-5 正史回收踩到一個和 M-9 錨定點語意衝突的真實 bug**:一開始直接呼叫 `SceneImportService.importScene` 匯入官方新場景,結果因為官方場景的 `storyOrder` 通常超過 `work.anchorStoryOrder`(後日談本來就是走在正史錨定點之前的),`importScene` 依 M-9 的知識邊界規則判斷「超前於錨定點,不投影」,導致回收回來的正史內容反而不會被角色「知道」——這暴露了 `anchorStoryOrder` 原本只追蹤「正史知識邊界」,但後日談的「現在」可以走到比它更遠的地方。修正方式:`CanonReclamationService.reclaim` 匯入後**直接呼叫 `ProjectionService.projectScene`**(不透過 `importScene` 的錨定閥門),並把 `anchorStoryOrder` 一併推進到覆蓋這批正史內容,讓「正史知識邊界」跟「已投影的正史範圍」重新同步。**這條經驗提醒之後任何跨群組重用既有服務時,要想清楚對方的前提假設(這裡是『匯入的場景預設尚未被角色經歷過』)是否仍然成立**,不成立時不能直接借用,要繞過去或明確重新同步狀態。
|
||||
> - **N-5 分支保留擋下重新匯入的錯誤處理**:一開始用裸的 `throw new Error(...)` 擋下已分岔作品的回收嘗試,NestJS 沒認得這個例外型別,回傳了無意義的 500——改成 `ConflictException`(409)。**這是本群組另一個「先用最省事的寫法、跑過一次真實請求才發現不對」的例子**,日後任何服務層拋出的「業務規則擋下」錯誤都該用 NestJS 的 `HttpException` 子類別(`BadRequestException`/`ConflictException`/`ForbiddenException` 等),不要用裸 `Error`——`RomanceEligibilityService`/`TimeFlowService` 已經是對的寫法,這次是漏了一處才補上。
|
||||
|
||||
### O. 立繪子系統
|
||||
|
||||
|
||||
Reference in New Issue
Block a user