feat(runtime): prepare safe context checkpoints
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
# Current-Branch Context Checkpoints
|
||||
|
||||
Status: design frozen for the Wave 7 implementation slice.
|
||||
|
||||
## Purpose
|
||||
|
||||
Long stories must stay inside the selected LAPP model's real context window without silently
|
||||
dropping the current input, triggered world-book entries, runtime facts, or recent committed
|
||||
scenes. A checkpoint is a disposable narrative cache. It is never an authoritative source for
|
||||
flags, relationships, promises, inventory, knowledge, checks, or branch structure.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Checkpoint persistence starts only after these contracts are represented in code:
|
||||
|
||||
1. The selected LAPP model's `context_window` and `max_output_tokens` reach the Runtime provider.
|
||||
2. The stable prompt prefix has a deterministic resource fingerprint.
|
||||
3. The narrative source path has a canonical ordered hash that covers committed public story
|
||||
content, not only `RuntimeState`.
|
||||
4. `Regenerate` excludes the replaced node from narrative history, constrains the replacement with
|
||||
the target node's committed post-state, creates a sibling on a new branch, and reuses existing
|
||||
hidden-check outcomes.
|
||||
|
||||
## Prompt budget
|
||||
|
||||
The provider computes one explicit budget before every model request:
|
||||
|
||||
```text
|
||||
hard input budget =
|
||||
model context window
|
||||
- requested output tokens
|
||||
- system prompt and tool schema
|
||||
- message framing overhead
|
||||
- reserved hidden-check continuation overhead
|
||||
- safety margin
|
||||
```
|
||||
|
||||
- Requested output is `min(model.max_output_tokens ?? 4096, 4096)`.
|
||||
- A missing context window uses a conservative 16,384-token V1 fallback, but the resulting
|
||||
`BudgetSource::Assumed` diagnostic must remain visible to the connection/settings layer. The
|
||||
fallback must not be silent.
|
||||
- Cross-provider V1 estimation treats every serialized UTF-8 byte as at most one token. This may
|
||||
compact early but must not optimistically overfill a model window.
|
||||
- Compression starts at 70% of the hard input budget and compacts back below a lower watermark.
|
||||
- The complete dynamic tail is budgeted first. If it does not fit by itself, the request fails with
|
||||
`DynamicTailTooLarge`.
|
||||
- At least one recent committed node remains verbatim. A single oversized node fails with
|
||||
`HistoryEntryTooLarge`; beat text is never truncated.
|
||||
- Every initial and hidden-check continuation `ChatInput` must remain within budget.
|
||||
|
||||
## Prompt layout
|
||||
|
||||
Prompt schema v3 will replace the current v2 history array with one production checkpoint-aware
|
||||
encoder:
|
||||
|
||||
```text
|
||||
prompt_schema_version
|
||||
stable_prefix
|
||||
branch_context
|
||||
checkpoint?
|
||||
raw_tail[]
|
||||
dynamic_tail
|
||||
```
|
||||
|
||||
`stable_prefix` remains byte-identical while the bound resources do not change. `dynamic_tail`
|
||||
always contains the current input and the complete safe state projection. A checkpoint replaces
|
||||
only a continuous oldest prefix of `raw_tail`.
|
||||
|
||||
## Checkpoint record
|
||||
|
||||
SQLite schema v3 adds a cache table keyed by the immutable host node, not by branch:
|
||||
|
||||
```sql
|
||||
CREATE TABLE context_checkpoints (
|
||||
story_id TEXT NOT NULL,
|
||||
at_node_id TEXT NOT NULL,
|
||||
covered_through_node_id TEXT NOT NULL,
|
||||
retained_from_node_id TEXT,
|
||||
checkpoint_schema_version INTEGER NOT NULL,
|
||||
prompt_schema_version INTEGER NOT NULL,
|
||||
stable_prefix_hash TEXT NOT NULL,
|
||||
summary_json TEXT NOT NULL,
|
||||
source_hash TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (story_id, at_node_id),
|
||||
FOREIGN KEY (story_id, at_node_id)
|
||||
REFERENCES nodes (story_id, node_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (story_id, covered_through_node_id)
|
||||
REFERENCES nodes (story_id, node_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (story_id, retained_from_node_id)
|
||||
REFERENCES nodes (story_id, node_id) ON DELETE CASCADE
|
||||
);
|
||||
```
|
||||
|
||||
- The nearest checkpoint is found only by walking the current node's `parent_id` chain.
|
||||
- Shared ancestors naturally share a checkpoint; sibling-only descendants are unreachable.
|
||||
- The summary is self-contained. A new checkpoint replaces the old summary instead of nesting a
|
||||
chain of summaries in the prompt.
|
||||
- Successful checkpoint generation may be persisted independently on the existing host node.
|
||||
Failure of the later story-generation call may leave this harmless cache in place.
|
||||
- Deleting every checkpoint must leave nodes, branch heads, materialized state, and `PlayerView`
|
||||
byte-identical.
|
||||
|
||||
## Source and resource hashes
|
||||
|
||||
The model never supplies either hash.
|
||||
|
||||
`stable_prefix_hash` covers the exact canonical stable-prefix encoding, including character,
|
||||
Persona, resource provenance, and prompt safety/version fields.
|
||||
|
||||
`source_hash` covers:
|
||||
|
||||
```text
|
||||
checkpoint source schema version
|
||||
prompt schema version
|
||||
stable_prefix_hash
|
||||
story id
|
||||
ordered root-to-covered sequence of:
|
||||
node id
|
||||
parent id
|
||||
player input
|
||||
public scene
|
||||
public character visual state
|
||||
committed beats
|
||||
```
|
||||
|
||||
Unselected suggestions, state delta, exact relationship values, checks, NPC private inventory,
|
||||
untriggered world-book content, provider responses, credentials, and chain-of-thought never enter
|
||||
the source manifest or compression request.
|
||||
|
||||
## Regenerate boundary
|
||||
|
||||
`Regenerate` is a full alternative rendering of one committed player choice:
|
||||
|
||||
- the replaced node is not included in the new prompt;
|
||||
- model history ends at the replaced node's parent, while the target node's authoritative
|
||||
post-state supplies only the already-committed facts and fixed qualitative check outcomes;
|
||||
- the replacement creates a sibling on a new runtime-generated branch;
|
||||
- hidden checks from the original action are supplied as qualitative fixed outcomes and are not
|
||||
rolled again;
|
||||
- a model cannot submit forged `RecordCheck` operations;
|
||||
- old nodes, descendants, and checkpoints remain immutable and become naturally unreachable from
|
||||
the new branch unless they are shared ancestors.
|
||||
|
||||
The sibling keeps the original player action identity, authoritative delta, terminal state, and
|
||||
exact hidden-check records. Those immutable records intentionally retain their original source-node
|
||||
provenance; the new branch is an alternative presentation of that same committed action, not a new
|
||||
roll or a new state transition.
|
||||
|
||||
## Summary safety
|
||||
|
||||
The summary may retain public causal order, actual player choices, public NPC actions, revealed
|
||||
facts with their certainty, unresolved conflicts, shared goals, and observable emotional residue.
|
||||
It may not become a trigger or rules input.
|
||||
|
||||
The summary must not contain:
|
||||
|
||||
- exact dice mechanics or relationship numbers;
|
||||
- private NPC knowledge, private inventory, or unrevealed item provenance;
|
||||
- hidden flags, clocks, event conditions, or untriggered world-book entries;
|
||||
- unselected suggestions, cancelled output, sibling-branch content, or inferred player thoughts;
|
||||
- credentials, request headers, raw provider bodies, or chain-of-thought.
|
||||
|
||||
The current player inventory projection must not be fed to the summarizer until ownership and
|
||||
acquisition fields have their own player-knowledge visibility boundary.
|
||||
|
||||
## Failure semantics
|
||||
|
||||
- No valid checkpoint and history above the high watermark returns `NeedsCompaction`; history is
|
||||
never silently shortened.
|
||||
- Invalid schema, source hash, stable-prefix hash, range, or summary size makes a checkpoint
|
||||
unusable and rebuildable.
|
||||
- Broken authoritative ancestry or state remains a hard store error and is not downgraded to a
|
||||
cache miss.
|
||||
- Cancelled, timed-out, rate-limited, or malformed compression produces no story node and moves no
|
||||
branch head.
|
||||
- A first load of a very long legacy branch uses bounded rolling chunks and keeps only the final
|
||||
self-contained summary.
|
||||
|
||||
## Required gates
|
||||
|
||||
- Exact window boundary, one-token overflow, unknown-model-limit fallback, and smaller-model switch.
|
||||
- Every normal and hidden-check request stays within the computed budget.
|
||||
- 500-node first compaction and incremental compaction from an existing checkpoint.
|
||||
- v2-to-v3 migration, rollback, restart recovery, cache deletion, and corruption rejection.
|
||||
- Root, shared-ancestor, pre/post-checkpoint fork, regenerate, and sibling-canary isolation.
|
||||
- Stable-prefix cache identity and one cache break only when a checkpoint rotates.
|
||||
- Hidden canaries for checks, exact relationships, NPC facts/items, untriggered resources,
|
||||
unselected suggestions, credentials, and provider bodies.
|
||||
@@ -0,0 +1,72 @@
|
||||
# M2 第七波 Windows 基线与上下文前置状态
|
||||
|
||||
日期:2026-07-29
|
||||
|
||||
## 本机 Windows 基线
|
||||
|
||||
- 已安装 Visual Studio 2022 Build Tools 17.14、MSVC x64 工具链与 Windows 11 SDK
|
||||
10.0.26100;`cl.exe`、`link.exe`、`rc.exe` 均可用。
|
||||
- `scripts/windows-smoke.ps1` 现在会通过 `vswhere` 选择具备 C++ 工具链的 Visual
|
||||
Studio,并自动载入 x64 开发环境。普通 PowerShell 不再需要先手工运行
|
||||
`VsDevCmd.bat`。
|
||||
- 使用现有应用图标生成 Tauri 的 Windows ICO、macOS ICNS、Linux PNG 及后续移动端
|
||||
图标集合,关闭了 Windows 资源编译缺少 `icon.ico` 的阻塞。
|
||||
- release 模式的 Windows 桌面程序已构建:
|
||||
`target/release/nana-story-app.exe`。
|
||||
|
||||
## Task 5 前置契约
|
||||
|
||||
### 模型预算
|
||||
|
||||
- `OpenLappChatExecutor` 从实际选中的 LAPP 模型读取 `context_window` 与
|
||||
`max_output_tokens`。
|
||||
- 缺少或无效模型元数据时显式标为 `Assumed`,使用 16,384 / 4,096 的保守 V1 回退;
|
||||
被应用上限或窗口边界收紧时标为 `Capped` 并保留原始来源。
|
||||
- 预算值只能通过校验构造,始终保证输出预算大于零且小于上下文窗口;上层可读取来源、
|
||||
fallback 与 cap 诊断。
|
||||
- 普通回合、隐藏判定初始调用和所有工具续调用使用同一个模型预算;单回合输出上限为
|
||||
4,096 tokens。
|
||||
|
||||
### 检查点来源
|
||||
|
||||
- 增加稳定前缀与叙事来源的强类型 SHA-256 指纹。
|
||||
- 稳定前缀显式覆盖角色、Persona、剧情模块与绑定世界书的版本来源;持久化读取只接受
|
||||
规范化的 `sha256:` 小写十六进制值。
|
||||
- 叙事来源只接受连续的根到目标节点路径,覆盖节点 ID、父节点、玩家实际输入、公开场景、
|
||||
角色视觉状态和完整演出节拍。
|
||||
- 状态 delta、精确关系、隐藏判定、NPC 私物、未选择建议和兄弟分支均不能进入来源投影。
|
||||
- [上下文检查点设计](../context-checkpoint-design.md) 已冻结预算、提示布局、SQLite v3、
|
||||
失效规则和安全边界。
|
||||
|
||||
### 重新生成
|
||||
|
||||
- `Regenerate` 的叙事历史截止到待替换节点的父节点;模型使用目标节点的安全 post-state
|
||||
与原判定定性结果生成兄弟节点,不再接收旧演出或骰点细节。
|
||||
- 原玩家行动标识、authoritative delta、隐藏判定、终局状态与物化状态原样复用;模型
|
||||
只能替换演出,也不能让成功 / 失败结果反转。
|
||||
- 终局节点允许重生成演出,但普通继续行动仍会被终局保护拦截。
|
||||
- 新版本使用独立分支并切为活动线路,旧节点、旧线路和后代保持不可变。
|
||||
- Memory 与 SQLite 均在单次原子操作中校验来源线路、节点、delta 和物化状态。
|
||||
|
||||
## 验证
|
||||
|
||||
- Rust workspace:171 项测试通过。
|
||||
- Runtime:83 项。
|
||||
- Store:44 项。
|
||||
- Tauri 后端:17 项。
|
||||
- Domain / Engine / Contracts:27 项。
|
||||
- `cargo clippy --workspace --all-targets -- -D warnings`:通过。
|
||||
- Rust 契约生成器 `--check`:通过。
|
||||
- Web:5 个测试文件 / 29 项测试、TypeScript 检查与生产构建通过。
|
||||
- 契约:25 份 Schema 与 TypeScript DTO 无漂移。
|
||||
- `pnpm tauri build --no-bundle`:通过,生成 Windows release 可执行文件。
|
||||
- 隔离 Demo 已启动,窗口枚举标题为《听娜娜讲故事》,进程保持响应并创建独立 SQLite
|
||||
存档。为重建 release 文件现已关闭该进程;隔离存档仍保留。自动截图组件不支持该
|
||||
Tauri 窗口,因此本报告不宣称视觉验收完成。
|
||||
|
||||
## 尚未关闭
|
||||
|
||||
- 当前执行环境仍会关闭 Gitea SSH 2222 连接,本地提交暂时不能推送。
|
||||
- SQLite v3 检查点表、同模型摘要工具、500 节点滚动压缩与实际预算编排仍属于 Task 5
|
||||
主体。
|
||||
- Demo 重启恢复、终局 / 双线路人工操作和真实 LAPP 在线调用仍待后续冒烟。
|
||||
@@ -28,9 +28,10 @@ git -C .\lapp-rs checkout 5ba3c659e1536ec4bee16340faca603940a5cb17
|
||||
|
||||
还需预先安装 Windows 的 Tauri 2 原生开发依赖、Microsoft C++ Build Tools、WebView2、
|
||||
Git、rustup、Rust 1.96.0 MSVC host(含 `rustfmt` 和 `clippy`)、Node.js 24+,以及
|
||||
`package.json` 指定版本的 pnpm。脚本只检查它们,不会自动安装或升级工具链。下文使用
|
||||
PowerShell 7 的 `pwsh`;脚本也只使用 Windows PowerShell 5.1 支持的语法,可将
|
||||
`pwsh` 换成 `powershell.exe`。
|
||||
`package.json` 指定版本的 pnpm。脚本会通过 `vswhere` 自动载入 x64 C++ 开发环境并
|
||||
检查 `cl.exe`、`link.exe` 与 `rc.exe`,但不会自动安装或升级工具链。下文使用 PowerShell
|
||||
7 的 `pwsh`;脚本也只使用 Windows PowerShell 5.1 支持的语法,可将 `pwsh` 换成
|
||||
`powershell.exe`。
|
||||
|
||||
## 2. 跑机械门禁
|
||||
|
||||
|
||||
Reference in New Issue
Block a user