@lemoncat7/dsh-partner 2.8.4 → 2.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +1 -1
  2. package/docs/THIRD_PARTY_LICENSES.md +4 -0
  3. package/docs/director-skills.md +8 -3
  4. package/docs/dsh-021-compatibility.md +9 -0
  5. package/docs/package-distribution.md +11 -0
  6. package/docs/releases/2.8.5.md +23 -0
  7. package/docs/releases/2.8.6.md +22 -0
  8. package/lib/channels/direct/message-format.js +13 -8
  9. package/lib/channels/direct/message-format.js.map +3 -3
  10. package/lib/client.js +13 -8
  11. package/lib/client.js.map +3 -3
  12. package/lib/skills/ai-camera-language-builtin.d.ts +3 -0
  13. package/lib/skills/ai-camera-language-builtin.d.ts.map +1 -0
  14. package/lib/skills/ai-camera-language-builtin.js +89 -0
  15. package/lib/skills/ai-camera-language-builtin.js.map +1 -0
  16. package/lib/skills/builtin.d.ts.map +1 -1
  17. package/lib/skills/builtin.js +2 -0
  18. package/lib/skills/builtin.js.map +1 -1
  19. package/lib/skills/story-builtins.js +2 -2
  20. package/lib/skills/story-builtins.js.map +1 -1
  21. package/package.json +53 -47
  22. package/resources/skills/ai-camera-language/manifest.json +26 -0
  23. package/resources/skills/ai-camera-language/references/camera-dictionary.md +315 -0
  24. package/resources/skills/ai-camera-language/references/prompt-workflow.md +186 -0
  25. package/resources/skills/ai-camera-language/references/video-summary.md +123 -0
  26. package/resources/skills/novel-game-creation/manifest.json +2 -245
  27. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/BUILD_BRIEF.md +0 -219
  28. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/.vercelignore +0 -3
  29. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/RUN.md +0 -138
  30. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/bg/compound_new_guofeng.webp +0 -0
  31. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/fate_coda.webp +0 -0
  32. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/meng.webp +0 -0
  33. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/pan.webp +0 -0
  34. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/pinger.webp +0 -0
  35. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/xuee.webp +0 -0
  36. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/yue.webp +0 -0
  37. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
  38. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_afterglow_five.webp +0 -0
  39. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_alliance.webp +0 -0
  40. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/public_day10.webp +0 -0
  41. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
  42. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/public_day5.webp +0 -0
  43. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/title_new_guofeng.webp +0 -0
  44. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/pan_pinger.webp +0 -0
  45. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/pan_xuee.webp +0 -0
  46. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/yue_meng.webp +0 -0
  47. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pan.webp +0 -0
  48. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pinger.webp +0 -0
  49. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/meng/explicit.webp +0 -0
  50. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/meng/prelude.webp +0 -0
  51. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/meng.webp +0 -0
  52. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/pan.webp +0 -0
  53. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/pinger.webp +0 -0
  54. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/xuee.webp +0 -0
  55. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/yue.webp +0 -0
  56. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/pan/explicit.webp +0 -0
  57. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/pan/prelude.webp +0 -0
  58. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/pinger/explicit.webp +0 -0
  59. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/pinger/prelude.webp +0 -0
  60. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/xuee/explicit.webp +0 -0
  61. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/xuee/prelude.webp +0 -0
  62. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/yue/explicit.webp +0 -0
  63. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/yue/prelude.webp +0 -0
  64. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/meng/night.webp +0 -0
  65. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/pan/night.webp +0 -0
  66. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/pinger/night.webp +0 -0
  67. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/xuee/night.webp +0 -0
  68. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/yue/night.webp +0 -0
  69. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
  70. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/scene/gate_collector.webp +0 -0
  71. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/css/new-guofeng.css +0 -1494
  72. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/css/style.css +0 -2508
  73. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/index.html +0 -18
  74. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/assets.js +0 -147
  75. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/audio.js +0 -332
  76. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/data.js +0 -7470
  77. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/engine.js +0 -13066
  78. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/main.js +0 -3051
  79. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/text.js +0 -32
  80. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/package.json +0 -11
  81. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/vercel.json +0 -23
  82. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/art/generated-art.json +0 -161
  83. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/example.json +0 -12
  84. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/qa/README.md +0 -26
  85. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/qa/playable-model.json +0 -99
  86. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/qa/verification.json +0 -41
  87. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/qa/verify_playable_model.mjs +0 -152
@@ -1,32 +0,0 @@
1
- // 玩家可见文案集中管理。路线对白在 data.js 中按设计节点集中管理。
2
-
3
- export const TEXT = Object.freeze({
4
- title: '金瓶梅·风月总账',
5
- subtitle: '二十日,五扇门。你说过的话,她们都记得。',
6
- identity: '你是西门庆。门外的债已经追到家门,门里有五个人等你把银子、谎话和旧承诺一件件说清。你可以偏爱,但不能叫她们替你撒谎。',
7
- rating: '18+ · 仅限成年人 · 含成人关系、轻度裸露与亲密内容',
8
- ageTitle: '十八岁以下,止步',
9
- ageBody: '本作只供成年人,含成人关系、肩背与腿部等轻度裸露、亲密场景与成熟主题。所有可进入亲密路线的人物均为成年虚构角色。',
10
- ageYes: '我已成年',
11
- ageNo: '离开',
12
- start: '进宅',
13
- continue: '接着玩',
14
- gallery: '场景册',
15
- restart: '从头再来',
16
- muteOn: '开声',
17
- muteOff: '静音',
18
- dayLead: '院里的事已摆到眼前。你先动哪一只手?',
19
- chooseVisit: '暮鼓响过,五处院门仍留着灯。今夜你去见谁?',
20
- sharedNightLead: '五个人都没走。账还没凉,酒已经热了——今夜是各回各院,还是在每个人都独立点头之后让五盏灯留在一处,就等你开口。',
21
- nightLead: '茶还热着,她的手在门闩上停了一会儿。接下来怎样,听她的。',
22
- morningLead: '天刚透进窗纸,昨夜没说尽的话已经到了门外。',
23
- banquetLead: '五只杯子都等着。这一次若把功劳、财权或证词分偏了,夜里就有一扇门先落闩。',
24
- galleryEmpty: '册子还是空的。等有人肯把门留给你,再来看。',
25
- galleryKeep: '从头再来,册页也不会丢。',
26
- saveNote: '每次落子都会存下;旧版账本不续接。',
27
- close: '合上',
28
- sceneContinue: '接着往下',
29
- endingRestart: '再走二十日',
30
- endingGallery: '翻场景册',
31
- reducedMotion: '已按系统设置减少动效',
32
- });
@@ -1,11 +0,0 @@
1
- {
2
- "name": "jinpingmei-fengyue-ledger",
3
- "private": true,
4
- "type": "module",
5
- "description": "《金瓶梅·风月总账》20日男性第一人称成人后宫关系模拟:五条深线、五院共约、联院协力与公开问责",
6
- "scripts": {
7
- "start": "python3 -m http.server 5173",
8
- "verify:model": "node ../../qa/verify_playable_model.mjs",
9
- "verify:visual": "python3 test/verify_visual.py --write-evidence"
10
- }
11
- }
@@ -1,23 +0,0 @@
1
- {
2
- "$schema": "https://openapi.vercel.sh/vercel.json",
3
- "headers": [
4
- {
5
- "source": "/assets/(.*)",
6
- "headers": [
7
- {
8
- "key": "Cache-Control",
9
- "value": "public, max-age=31536000, immutable"
10
- }
11
- ]
12
- },
13
- {
14
- "source": "/(.*)\\.(js|css)",
15
- "headers": [
16
- {
17
- "key": "Cache-Control",
18
- "value": "public, max-age=3600, must-revalidate"
19
- }
20
- ]
21
- }
22
- ]
23
- }
@@ -1,161 +0,0 @@
1
- {
2
- "schemaVersion": 3,
3
- "generatedAt": "2026-08-25",
4
- "toolVersion": "Codex built-in image_gen",
5
- "usage": "《金瓶梅·风月总账》四十一张运行图的二维新国风彻底重塑",
6
- "reason": "旧棕黑 CG、古画册页样张、真人古装样张和现代时尚样张分别被判定为太暗、太旧、太真实和太现代。最终批准方向为完整晚明世界中的成熟理想化二维叙事游戏插画。",
7
- "externalStyleResearch": {
8
- "source": "https://github.com/freestylefly/awesome-gpt-image-2",
9
- "principles": [
10
- "Case 213:首先读作可进入的古代游戏空间,不借 MMO HUD、3D 真人渲染或暗街",
11
- "Case 290:角色卡级身份色、器物锚点和即时情绪,不借 SSR 文案、镭射边框或暗底",
12
- "Case 9:现代、多彩、明亮通透的国潮色光和留白,不借现代城市与宣传构图",
13
- "Case 230:朱红、青蓝、鎏金的克制功能配色,不借品牌海报和现代地标"
14
- ],
15
- "rejected": [
16
- "Case 206/242 式古画与书签媒介",
17
- "Case 343/514 式现代时尚与硬边艺术",
18
- "Case 207 式暗黑真人封面"
19
- ],
20
- "copyrightBoundary": "未复制或提交参考库案例图;只借媒介裁决、游戏化识别、色光与留白方法。"
21
- },
22
- "approvedStyleFrame": {
23
- "asset": "app/assets/cg/group/title_new_guofeng.webp",
24
- "sourceOutput": "codex-imagegen:exec-e1603504-1f87-4923-8f7d-7b6a45751299.png",
25
- "promptSummary": "五名成年女性置于通透明代春庭;二维数字手绘、成熟理想化面孔、流动丝绸、朱砂石青青绿桃粉;人物居中右,左侧留真实 HTML 标题区。",
26
- "required": [
27
- "完整晚明建筑、服饰、发式、器物与关系空间",
28
- "二维手绘游戏 CG 与可想象的人物材质",
29
- "白日中高明度;夜景同时保留蓝紫月光和暖灯",
30
- "五人脸型、成熟度、姿态、衣色与器物互异",
31
- "画面内不承担正文排版"
32
- ],
33
- "forbidden": [
34
- "真人摄影、3D 皮肤、现代时尚棚拍或品牌海报",
35
- "古画复刻、工笔册页、宣纸旧化或全局黄褐滤镜",
36
- "暗黑底、重暗角、全屏烟雾或不可读夜景",
37
- "幼态、未成年人、胁迫、露点或性行为",
38
- "现代物件、文字、标志或水印"
39
- ]
40
- },
41
- "identityMasters": [
42
- {
43
- "character": "吴月娘",
44
- "path": "app/assets/heroine/yue/night.webp",
45
- "sourceOutput": "codex-imagegen:exec-bd1047ec-fc3b-40e4-ac8a-1d0acfe1e174.png",
46
- "anchors": "石青与象牙、总账、铜钥匙"
47
- },
48
- {
49
- "character": "潘金莲",
50
- "path": "app/assets/heroine/pan/night.webp",
51
- "sourceOutput": "codex-imagegen:exec-f521d0b2-b51a-428a-90cb-c3cbb0e5592e.png",
52
- "anchors": "朱砂与暖粉、团扇、回看"
53
- },
54
- {
55
- "character": "李瓶儿",
56
- "path": "app/assets/heroine/pinger/night.webp",
57
- "sourceOutput": "codex-imagegen:exec-314abee8-24ce-4494-9430-22b3122424df.png",
58
- "anchors": "象牙与浅金、漆匣、私钥"
59
- },
60
- {
61
- "character": "孟玉楼",
62
- "path": "app/assets/heroine/meng/night.webp",
63
- "sourceOutput": "codex-imagegen:exec-230a5eec-86c4-4923-8955-3715328c4cbe.png",
64
- "anchors": "青绿与藕荷、名帖、曲谱、空席"
65
- },
66
- {
67
- "character": "孙雪娥",
68
- "path": "app/assets/heroine/xuee/night.webp",
69
- "sourceOutput": "codex-imagegen:exec-262bebeb-8053-46d6-a1ff-5afd4a8811fa.png",
70
- "anchors": "琥珀与深青、米账、仓绳、灶火"
71
- },
72
- {
73
- "character": "李娇儿",
74
- "path": "app/assets/household/li_jiaoer.webp",
75
- "sourceOutput": "codex-imagegen:exec-24cda691-c334-48cc-b23b-e4feefe8e8c8.png",
76
- "anchors": "梅紫与赭金、契纸、银秤、印匣"
77
- }
78
- ],
79
- "batches": [
80
- {
81
- "usage": "世界与入口",
82
- "promptSummary": "春日高机位宅院和明亮门槛催账;压力由契纸、人物姿态和受阻门槛表达。",
83
- "assets": [
84
- ["app/assets/bg/compound_new_guofeng.webp", "codex-imagegen:exec-c623d43c-e15a-4472-b9f4-f748ad848b56.png"],
85
- ["app/assets/scene/gate_collector.webp", "codex-imagegen:exec-a7b0d1b1-e316-41b6-ad57-86d009bfbada.png"]
86
- ]
87
- },
88
- {
89
- "usage": "五人前奏与关系落定",
90
- "promptSummary": "同一身份母版的夜间关系 CG;以目光、开掌、钥匙、出口和共享日用表达成年人的主动选择,完整着装,无性行为。",
91
- "assets": [
92
- ["app/assets/cg/yue/prelude.webp", "codex-imagegen:exec-1ac36cb6-30a6-413d-812c-4b1b482b8b2a.png"],
93
- ["app/assets/cg/yue/explicit.webp", "codex-imagegen:exec-65126819-70a1-4773-9759-706eaf51e6e1.png"],
94
- ["app/assets/cg/pan/prelude.webp", "codex-imagegen:exec-dcd0c85d-b0b1-4ae3-973f-40f8e2fa65e5.png"],
95
- ["app/assets/cg/pan/explicit.webp", "codex-imagegen:exec-ae6c99ab-27ce-4652-81d1-2af468e8260a.png"],
96
- ["app/assets/cg/pinger/prelude.webp", "codex-imagegen:exec-01830d1b-2d87-4e97-b97c-92f15ced287c.png"],
97
- ["app/assets/cg/pinger/explicit.webp", "codex-imagegen:exec-a985ad93-a833-4272-b41e-ed493997eca4.png"],
98
- ["app/assets/cg/meng/prelude.webp", "codex-imagegen:exec-f6a3adb4-0335-497b-8509-ee2c09f61780.png"],
99
- ["app/assets/cg/meng/explicit.webp", "codex-imagegen:exec-00c1529b-543e-42fc-8dad-3de16000a529.png"],
100
- ["app/assets/cg/xuee/prelude.webp", "codex-imagegen:exec-48622697-5ef3-443e-9a34-83e4a9c25859.png"],
101
- ["app/assets/cg/xuee/explicit.webp", "codex-imagegen:exec-216df76e-b036-443a-9e70-529f2b12cdba.png"]
102
- ]
103
- },
104
- {
105
- "usage": "五人公开关系与共同办事",
106
- "promptSummary": "五人以交叉视线、递证、按账、留座和轮值器物形成真实关系动作,不做并排群像。",
107
- "assets": [
108
- ["app/assets/cg/group/public_day5.webp", "codex-imagegen:exec-32d97fcc-b83b-41f4-8bae-d8e5bf286910.png"],
109
- ["app/assets/cg/group/public_day10.webp", "codex-imagegen:exec-60f375a8-ae0f-426d-9de2-fdbda4649142.png"],
110
- ["app/assets/cg/group/public_day15.webp", "codex-imagegen:exec-0be274e2-1562-4cb6-9354-7d437b27aa43.png"],
111
- ["app/assets/cg/group/inner_court_alliance.webp", "codex-imagegen:exec-7651ef0d-f27b-4ed1-86f7-7aaa5d049f99.png"],
112
- ["app/assets/cg/group/inner_court_accord_five.webp", "codex-imagegen:exec-1bc7fb07-e6df-4974-b802-69f6f32cf44b.png"],
113
- ["app/assets/cg/group/inner_court_afterglow_five.webp", "codex-imagegen:exec-19692392-08c1-4eeb-8c63-ea86b1a2e07b.png"]
114
- ]
115
- },
116
- {
117
- "usage": "五组双院结果",
118
- "promptSummary": "两名对应成年女性共同作用于一件证物;脸、衣色、器物和方法冲突继承身份母版。",
119
- "assets": [
120
- ["app/assets/cg/joint/yue_pan.webp", "codex-imagegen:exec-74e2b0e8-c46f-45c7-8ee6-e4ee651bd56d.png"],
121
- ["app/assets/cg/joint/yue_pinger.webp", "codex-imagegen:exec-35f35c39-8dfc-40b2-bfaa-f6071929cdb0.png"],
122
- ["app/assets/cg/joint/pan_pinger.webp", "codex-imagegen:exec-a0bb4acc-0a64-444d-9c5a-30b2d0fad595.png"],
123
- ["app/assets/cg/joint/yue_meng.webp", "codex-imagegen:exec-02c5f590-a171-4fa0-8a79-b0324079a512.png"],
124
- ["app/assets/cg/joint/pan_xuee.webp", "codex-imagegen:exec-5af38352-b063-4dbe-9b4a-af807faa2f94.png"]
125
- ]
126
- },
127
- {
128
- "usage": "五张个人终章与命数空宅",
129
- "promptSummary": "人物用钥匙、席位、退路、名帖、停灶和轮值执行最后规则;命数页以无人空宅、六把钥匙和五张名签承接固定历史。",
130
- "assets": [
131
- ["app/assets/cg/finale/yue.webp", "codex-imagegen:exec-a0272894-bfa1-44e7-a15f-a749322db57f.png"],
132
- ["app/assets/cg/finale/pan.webp", "codex-imagegen:exec-4ab8a42b-3d8b-4f81-8864-82ec3366b40f.png"],
133
- ["app/assets/cg/finale/pinger.webp", "codex-imagegen:exec-8c08054f-b78f-492d-8187-6b80dcaecc73.png"],
134
- ["app/assets/cg/finale/meng.webp", "codex-imagegen:exec-d3764cf3-67a3-4ae7-b98d-1b4f26083aea.png"],
135
- ["app/assets/cg/finale/xuee.webp", "codex-imagegen:exec-ed7203e7-bcbb-4829-b1c1-417ee44cedf8.png"],
136
- ["app/assets/cg/finale/fate_coda.webp", "codex-imagegen:exec-c29af8f8-fca4-4f71-aef0-5af5a4dd4d2a.png"]
137
- ],
138
- "editNote": "月娘、瓶儿、雪娥终章使用 image_gen 二次编辑清除模型伪文字。"
139
- },
140
- {
141
- "usage": "五张中局转折",
142
- "promptSummary": "空主位与轮值钥匙、矛盾口风与封口银、破锁空匣与退路、名帖网络、冷灶拒印分别成为可读危机。",
143
- "assets": [
144
- ["app/assets/cg/milestone/yue.webp", "codex-imagegen:exec-a84bb537-8fc2-4844-9d40-f6552b28fb4c.png"],
145
- ["app/assets/cg/milestone/pan.webp", "codex-imagegen:exec-debb4c41-2c4e-41b8-8eff-3e1a272184ec.png"],
146
- ["app/assets/cg/milestone/pinger.webp", "codex-imagegen:exec-43cace36-0bee-41d1-aa84-798882ad6d49.png"],
147
- ["app/assets/cg/milestone/meng.webp", "codex-imagegen:exec-3adba92b-e8cf-4103-adf7-ac81026dbf41.png"],
148
- ["app/assets/cg/milestone/xuee.webp", "codex-imagegen:exec-de699d51-80ee-4cc5-93d7-c0553f705f0f.png"]
149
- ],
150
- "editNote": "月娘、玉楼、雪娥转折图使用 image_gen 二次编辑清除模型伪文字。"
151
- }
152
- ],
153
- "postProcessing": {
154
- "command": "cwebp -q 88 -m 6 -metadata none",
155
- "assetCount": 41,
156
- "totalBytes": 10938766,
157
- "intermediatePolicy": "image_gen 原始 PNG 留在工具缓存,不复制进仓库;运行目录只保留四十一张新 WebP。",
158
- "reproducible": false
159
- },
160
- "acceptance": "人工巡检身份母版、个人夜景、五人群像、双院结果、终章与中局转折;浏览器 QA 另验证四十一张 URL 解码、旧资产清零、目标视口、流程输入和控制台。"
161
- }
@@ -1,12 +0,0 @@
1
- {
2
- "language": "zh-Hans",
3
- "title": "金瓶梅 · 风月总账",
4
- "targetFinish": "playable-prototype",
5
- "source": {
6
- "chapters": 100,
7
- "headingPattern": "^\\s*第([〇零○一二三四五六七八九十百]+)回\\s+(.+?)\\s*$",
8
- "numeral": "chinese"
9
- },
10
- "coverageHeading": "全书覆盖",
11
- "citationPattern": "第(\\d{1,3})(?:\\s*[-–—至]\\s*(\\d{1,3}))?回"
12
- }
@@ -1,26 +0,0 @@
1
- # QA 证据说明
2
-
3
- 本 example 的当前权威 QA 命令是:
4
-
5
- ```bash
6
- cd examples/jin-ping-mei/build/app
7
- python3 test/verify_visual.py --write-evidence
8
- ```
9
-
10
- 命令开始时先把 `verification.json` 原子写为未运行的失败态,完整路径成功后才原子更新
11
- `verification.json` 与严格 schema-v1 `evidence/run.json`。浏览器由脚本自启临时本地服务;全部推进使用
12
- Playwright `locator.click` 且不使用 `force`,DOM 只负责读取下一项可见可行控件的 selector。
13
-
14
- 当前路径覆盖年龄门、五院标题、五档标题/首日布局、首日真实夜访与选择后章,再以每屏第一项可行
15
- 主动作快速到达一个第二十日失稳结局并换 seed 重开。只保留标题、首日、夜访和结局四张代表图;该路径不
16
- 穷举其他结局,不复测全键盘或五档完整二十日矩阵,也不证明主观吸引力。
17
-
18
- 仓内另提供一条不依赖浏览器的可执行改编合同:
19
-
20
- ```bash
21
- node examples/jin-ping-mei/qa/verify_playable_model.mjs
22
- ```
23
-
24
- 它对照 `playable-model.json` 与实际 `data.js` / `engine.js`,验证两种正堂起手的差异、三拍回响、非空
25
- 回调载荷、证物来源与消费边界、同 seed 确定性、存档重演与分支污染拒绝。它不调用第 5/15/20 日、
26
- 结局、一月笺或命数消费者,是设计回归,不冒充上面的真实渲染、输入或完整二十日证据。
@@ -1,99 +0,0 @@
1
- {
2
- "schemaVersion": 1,
3
- "source": "design/GAME_DESIGN.md#10-可执行切片合同",
4
- "experienceProfile": "narrative-led",
5
- "buildPath": "custom",
6
- "versions": {
7
- "contentRevision": "NOT_AVAILABLE: 现有候选早于内容修订号拆分",
8
- "rulesRevision": "NOT_AVAILABLE: 现有候选早于规则修订号拆分",
9
- "saveSchema": 67
10
- },
11
- "stateSurface": {
12
- "resources": {
13
- "initial": {
14
- "silver": 220,
15
- "power": 2,
16
- "repute": 3,
17
- "exposure": 0,
18
- "strain": 0,
19
- "house": 65
20
- }
21
- },
22
- "relationshipActors": [
23
- "wu_yueniang",
24
- "pan_jinlian",
25
- "li_pinger",
26
- "meng_yulou",
27
- "sun_xuee"
28
- ],
29
- "dayActions": ["ledger", "office", "listen", "banquet"],
30
- "eventLog": "history",
31
- "knowledgeLedger": "secrets"
32
- },
33
- "openingActions": [
34
- {
35
- "id": "opening_open_ledger",
36
- "expected": {
37
- "flag": "opened_true_ledger",
38
- "absentFlag": "heard_all_five",
39
- "house": 69,
40
- "bonds": {
41
- "wu_yueniang|li_pinger": 6,
42
- "wu_yueniang|sun_xuee": 7
43
- }
44
- }
45
- },
46
- {
47
- "id": "opening_hear_five",
48
- "expected": {
49
- "flag": "heard_all_five",
50
- "absentFlag": "opened_true_ledger",
51
- "house": 65,
52
- "bonds": {
53
- "pan_jinlian|meng_yulou": -1,
54
- "pan_jinlian|sun_xuee": 10
55
- }
56
- }
57
- }
58
- ],
59
- "callbacks": [
60
- "publicEcho",
61
- "day15Text",
62
- "finalText",
63
- "endingTexts",
64
- "epilogueTexts",
65
- "fateText"
66
- ],
67
- "knowledgeProbe": {
68
- "opening": "opening_open_ledger",
69
- "action": "listen",
70
- "factId": "missing_fifty",
71
- "source": "正堂开账听来的口风",
72
- "confidence": "待互证",
73
- "expiresOn": 7
74
- },
75
- "replay": {
76
- "seed": 42,
77
- "openingAftermathBeats": 3,
78
- "expectedDayPhase": "day",
79
- "tamperedStateMustBeRejected": true
80
- },
81
- "validationPaths": {
82
- "primary": {
83
- "opening": "opening_open_ledger",
84
- "aftermathAdvances": 3,
85
- "expectedFlag": "opened_true_ledger",
86
- "absentFlag": "heard_all_five",
87
- "expectedHouse": 69,
88
- "expectedFeedback": "openingMemory 非空回调载荷存在"
89
- },
90
- "adjacentCounterexample": {
91
- "opening": "opening_hear_five",
92
- "aftermathAdvances": 3,
93
- "expectedFlag": "heard_all_five",
94
- "absentFlag": "opened_true_ledger",
95
- "expectedHouse": 65,
96
- "expectedFeedback": "openingMemory 非空回调载荷存在且分支摘要不同"
97
- }
98
- }
99
- }
@@ -1,41 +0,0 @@
1
- {
2
- "schemaVersion": 3,
3
- "status": "PASS",
4
- "verify": {
5
- "command": "cd examples/jin-ping-mei/build/app && python3 test/verify_visual.py --write-evidence",
6
- "exitCode": 0
7
- },
8
- "completeRun": {
9
- "id": "jin-ping-mei-visual-reconstruction-2026-09-05",
10
- "cleanContext": true,
11
- "terminal": "day-20-unstable-ending",
12
- "restart": "day-1-opening-new-seed",
13
- "evidence": "qa/evidence/run.json"
14
- },
15
- "checks": {
16
- "launch": "PASS",
17
- "render": "PASS",
18
- "input": "PASS",
19
- "coreLoop": "PASS",
20
- "outcome": "PASS",
21
- "restart": "PASS"
22
- },
23
- "limitations": [
24
- {
25
- "scope": "路径覆盖",
26
- "reason": "快速路径在每屏选择第一项可行主动作,只到达一个失稳结局;没有穷举其他选项或结局。"
27
- },
28
- {
29
- "scope": "输入覆盖",
30
- "reason": "本次使用 Playwright locator.click 且不使用 force;没有复测 2026-08 的全键盘正常速度路径。"
31
- },
32
- {
33
- "scope": "响应式覆盖",
34
- "reason": "五档视口覆盖标题与首日,390×844 和 1280×800 覆盖夜访,1280×800 覆盖本次结局;未声明五档完整二十日矩阵。"
35
- },
36
- {
37
- "scope": "体验判断",
38
- "reason": "自动化只证明当前候选可启动、渲染、输入、走到结果并重开,不判断主观吸引力、长期平衡或其他浏览器。"
39
- }
40
- ]
41
- }
@@ -1,152 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { createHash } from 'node:crypto';
3
- import { readFileSync } from 'node:fs';
4
- import { dirname, resolve } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
-
7
- const here = dirname(fileURLToPath(import.meta.url));
8
- const contract = JSON.parse(readFileSync(resolve(here, 'playable-model.json'), 'utf8'));
9
- const data = await import(resolve(here, '../build/app/js/data.js'));
10
- const engine = await import(resolve(here, '../build/app/js/engine.js'));
11
-
12
- const digest = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
13
- const assertText = (value, label) => assert.equal(typeof value === 'string' && value.length > 0, true, label);
14
- const ROUTE_RULES_DIGEST = '3b3c6bd6dc565ff05825d16738ef3a76ad87bd1392d7d149794dc55ff8c005fa';
15
-
16
- function routeRulesProjection() {
17
- const route = Object.fromEntries(Object.entries(data.ROUTE_CHOICES).map(([heroine, steps]) => [
18
- heroine,
19
- steps.map((choices) => choices.map((choice) => ({
20
- id: choice.id,
21
- effects: choice.effects,
22
- condition: choice.condition ?? null,
23
- }))),
24
- ]));
25
- const accord = Object.fromEntries(Object.entries(data.ACCORD_CHOICES).map(([heroine, choice]) => [
26
- heroine,
27
- { id: choice.id, effects: choice.effects, condition: choice.condition ?? null },
28
- ]));
29
- const shared = data.SHARED_NIGHT_CHOICES.map((choice) => ({
30
- id: choice.id,
31
- effects: choice.effects ?? null,
32
- condition: choice.condition ?? null,
33
- }));
34
- return { route, accord, shared };
35
- }
36
-
37
- function finishOpening(state) {
38
- let beats = 0;
39
- while (state.phase === 'opening_aftermath') {
40
- const current = engine.currentOpeningAftermath(state);
41
- assert.ok(current?.current, '开场后章必须存在当前拍');
42
- assert.equal(engine.advanceOpeningAftermath(state).ok, true);
43
- beats += 1;
44
- }
45
- assert.equal(beats, contract.replay.openingAftermathBeats);
46
- assert.equal(state.phase, contract.replay.expectedDayPhase);
47
- }
48
-
49
- function runOpening(action) {
50
- const state = engine.newGame(contract.replay.seed);
51
- assert.deepEqual(state.resources, contract.stateSurface.resources.initial);
52
- assert.deepEqual(Object.keys(state.relations), contract.stateSurface.relationshipActors);
53
- const eventLog = state[contract.stateSurface.eventLog];
54
- const knowledgeLedger = state[contract.stateSurface.knowledgeLedger];
55
- assert.equal(Array.isArray(eventLog), true, '事件日志字段必须指向数组');
56
- assert.equal(Array.isArray(knowledgeLedger), true, '知识账字段必须指向数组');
57
- assert.equal(engine.chooseOpening(state, action.id).ok, true);
58
- assert.equal(eventLog.length, 1);
59
- assert.deepEqual(eventLog[0], {
60
- day: 1,
61
- type: 'opening',
62
- choice: action.id,
63
- public: true,
64
- });
65
- assert.equal(state.flags[action.expected.flag], true);
66
- assert.equal(state.flags[action.expected.absentFlag], undefined, '未选起手不得污染 history/flags');
67
- assert.equal(state.resources.house, action.expected.house);
68
- for (const [key, value] of Object.entries(action.expected.bonds)) assert.equal(state.bonds[key], value, key);
69
-
70
- finishOpening(state);
71
- const memory = engine.openingMemory(state);
72
- assert.equal(memory.choice, action.id);
73
- for (const field of contract.callbacks) {
74
- if (field === 'endingTexts' || field === 'epilogueTexts') {
75
- assert.equal(Object.keys(memory[field]).length, 5, `${field} 应提供五份长期回调载荷`);
76
- for (const text of Object.values(memory[field])) assertText(text, `${field} 不得为空`);
77
- } else assertText(memory[field], `${field} 不得为空`);
78
- }
79
-
80
- const options = engine.dayOptions(state);
81
- assert.deepEqual(options.map(({ id }) => id), contract.stateSurface.dayActions);
82
- assert.equal(options.filter(({ resolvesPressure }) => resolvesPressure).length >= 1, true);
83
-
84
- const serialized = engine.serialize(state);
85
- assert.deepEqual(engine.deserialize(serialized), state, '合法 snapshot 必须从 history 重演通过');
86
- const tampered = JSON.parse(serialized);
87
- tampered.flags[action.expected.absentFlag] = true;
88
- assert.equal(engine.deserialize(JSON.stringify(tampered)), null, '未选分支注入必须拒读');
89
-
90
- return {
91
- state: engine.snapshot(state),
92
- digest: digest(state),
93
- };
94
- }
95
-
96
- function verifyKnowledgeLedger() {
97
- const probe = contract.knowledgeProbe;
98
- const state = engine.newGame(contract.replay.seed);
99
- assert.equal(engine.chooseOpening(state, probe.opening).ok, true);
100
- finishOpening(state);
101
- assert.equal(engine.chooseDayAction(state, probe.action).ok, true);
102
- assert.equal(state[contract.stateSurface.knowledgeLedger].includes(probe.factId), true, '知识账必须记录已获知事实');
103
- const fact = engine.secretInventory(state).find(({ id }) => id === probe.factId);
104
- assertText(fact?.label, '证物标签不得为空');
105
- assert.deepEqual(fact, {
106
- id: probe.factId,
107
- label: fact.label,
108
- source: probe.source,
109
- confidence: probe.confidence,
110
- expiresOn: probe.expiresOn,
111
- expired: false,
112
- selected: false,
113
- });
114
- assert.equal(state.selectedSecret, null, '获得事实不等于已经选择提交');
115
- assert.deepEqual(state.secretsUsed, [], '未选择的事实不得被动作标成已消费');
116
- assert.equal(state.history.at(-1).secretUsed, null, '事件日志不得伪造事实消费');
117
-
118
- const tampered = JSON.parse(engine.serialize(state));
119
- tampered.secretsUsed.push(probe.factId);
120
- assert.equal(engine.deserialize(JSON.stringify(tampered)), null, '伪造已消费事实必须拒读');
121
- }
122
-
123
- assert.equal(contract.schemaVersion, 1);
124
- assert.equal(contract.source, 'design/GAME_DESIGN.md#10-可执行切片合同');
125
- assert.equal(contract.experienceProfile, 'narrative-led');
126
- assert.equal(contract.buildPath, 'custom');
127
- assert.match(contract.versions.contentRevision, /^NOT_AVAILABLE:/);
128
- assert.match(contract.versions.rulesRevision, /^NOT_AVAILABLE:/);
129
- assert.equal(engine.SAVE_VERSION, contract.versions.saveSchema);
130
- assert.equal(contract.replay.tamperedStateMustBeRejected, true, '合同必须门控伪造状态拒读');
131
- assert.equal(contract.validationPaths.primary.opening, contract.openingActions[0].id);
132
- assert.equal(contract.validationPaths.adjacentCounterexample.opening, contract.openingActions[1].id);
133
- for (const [index, path] of Object.values(contract.validationPaths).entries()) {
134
- const action = contract.openingActions[index];
135
- assert.equal(path.aftermathAdvances, contract.replay.openingAftermathBeats);
136
- assert.equal(path.expectedFlag, action.expected.flag);
137
- assert.equal(path.absentFlag, action.expected.absentFlag);
138
- assert.equal(path.expectedHouse, action.expected.house);
139
- assertText(path.expectedFeedback, '固定验证路径必须声明预期反馈');
140
- }
141
- assert.deepEqual(Object.keys(data.DAY_ACTIONS), contract.stateSurface.dayActions);
142
- assert.deepEqual(data.HEROINE_IDS, contract.stateSurface.relationshipActors);
143
- assert.deepEqual(data.OPENING_CHOICES.map(({ id }) => id), contract.openingActions.map(({ id }) => id));
144
- assert.equal(digest(routeRulesProjection()), ROUTE_RULES_DIGEST, '文案改写不得改动五条路线、五院约或共享终夜的 id/condition/effects');
145
-
146
- const firstRuns = contract.openingActions.map(runOpening);
147
- const replayRuns = contract.openingActions.map(runOpening);
148
- assert.deepEqual(replayRuns.map(({ digest: value }) => value), firstRuns.map(({ digest: value }) => value));
149
- assert.notEqual(firstRuns[0].digest, firstRuns[1].digest, '两条起手必须产生可区分状态');
150
- verifyKnowledgeLedger();
151
-
152
- console.log('可执行改编合同:2 条起手、3 拍回响、非空回调载荷、知识来源、确定性重放与分支污染拒绝均一致');