Files
dsh_ai1net_server/归档/代码与机制清退/接续入口_StoryForge插件线_20260920.md.removed-20260923
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

134 lines
24 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 接续入口 · StoryForge 插件线(2026-09-20 立)
> 🔴 **工作区**:E:/ProgramData/AI技能/dsh-plugin-forge —— ⚠️ **本文件是外来副本**(2026-09-21 19:37 由 forge 侧「两处同改」产生)。StoryForge 线自己的家是 `dsh-plugin-forge`,本副本**应迁移并删除**;`state.py` 已加归属校验(2026-09-21),会把它标成「**外来线**」并排除出 §2 / 口令。⛔ 今后**不再两处同改**。
> **唯一执行依据 = 本文件 §0 最新行 + §2 首行(本棒)**。⛔ 勿重做已完成棒、⛔ 勿跳序。
> 方案单一来源 = `E:\ProgramData\AI技能\dsh-plugin-forge\StoryForge改造为dsh插件_可行性与落地方案_20260920.md`(**v2**,只读 §二 / §三 / §四 / §七 / §九)
> **as-built 说明(已建成什么样)= `E:\ProgramData\AI技能\dsh-plugin-forge\StoryForge插件_改造说明与数据落点_20260921.md`** —— 含**全部改造点**(上游 3 处 + 插件包 8 件 + 七条硬契约)、**平台数据库对接现状与路径**、**数据落点主表 + 铁律六条**、现状差距。⚠️ 该文件是**当前事实的唯一汇总面**,回答"改了什么 / 数据放哪"先看它
> 单子落点 = `D:\github\dsh_shenxian\dsh-server-docs\交接单\StoryForge插件线-序*.md`(**权威**)|本线目录留一份同步副本
> 目标一句话:把 StoryForge(上游 v3.9.1,AI 小说创作工具)**不 fork、只改 3 处**地做成 dsh 业务插件(形态 = 同源 iframe 内嵌)。
> 🔴 **权威接续入口 = `E:/ProgramData/AI技能/dsh-plugin-forge/接续入口_StoryForge插件线_20260920.md`(StoryForge 线自己的工作区)**;**本工作区(aliyun-dsh-server)这份是残留副本 —— ⛔ 不要在这里改、⛔ 不要两处同改**,要改只改 forge 那份(归属三律:入口只允许一份)。
---
## §0 定序(线内串行,勿跳)
| 序 | 单子 | 状态 |
|---|---|---|
| **1** | `StoryForge插件线-序1-上游改造与插件骨架.md` | ✅ **已落**(2026-09-20 23:45 → 09-21 00:05 · 执行棒 `StoryForge插件线-序1执行棒`) |
| **2** | `StoryForge插件线-序2-部署与实例验收.md` | 🟡 **部分执行**(2026-09-21 00:42 → 01:1x · 执行棒 `StoryForge插件线-序2执行棒`)—— **S0–S5 ✅ 全绿**:host 半区 `/storyforge/*` 前缀路由已落、本机等价验收 **`4/4 PASS`(50 断言 0 失败)**、`npm pack` **1,754,963 B** / md5 `dca573b57814de6c205b7be6d3772760`、候选池 **200 / `blocked=0`**(未用 trust)、**实机证明插件被加载并注册路由**(标记 ×4)、实装 md5 逐字节一致、属主 114801 无 root。🔴 **S6 卡在平台 `mine/apply` 的「重启+探活」腿**(终态 `failed`、平台已 `restoreProfile` 自动回滚 ⇒ 实例当前**未启用**;真机判据 1–4 **未验**);四条证据判定**与本插件无关**。⛔ **链条已暂停** —— 见 §1 末条 + §2 |
| 3 | 序3 — LLM 代理与配额(`/storyforge/api/llm/*`,**含拍板项**) | ⛔ **未立项**,等用户拍板;🔴 **新增硬前置**:须先解 §2 记录的 S6 阻塞(平台启用链路探活腿),否则装上亦看不见效果 |
| **跨线** | `对接单_平台侧-mine-apply重启探活腿异常_20260921.md`(本线 → 平台线) | ⏳ **已出,待平台侧评估**(2026-09-21 06:2x 规划棒2)。落点 = **本工作区**(按用户 2026-09-21 约定) |
**⛔ 全序遇到即停(写死)**:凡需决定「**模型 KEY 来源 / 每用户配额 / 是否由平台承担模型调用成本**」⇒ **立刻停下报告,不得自拍**(属 §1 边界外,归序3)。
**边界(全序通用)**:不 commit / 不 push;⛔ **本线文档只落本工作区,不写 `dsh-server-docs`**(因此**不涉及**共享锁);⛔ 不碰平台 `src/`;上传候选池 / 启用实例 / 重启**生产服务** / scp **到生产服务器** 属序2(序1 内禁止)。✅ **scp 到本机 Ubuntu 测试靶机不受此限**(见下)。
**🔴 验收环境(用户 2026-09-21 明令:「开发完成后在本机 ubuntu 上进行测试」)**
| 项 | 值 |
|---|---|
| 靶机 | **本机 WSL2 · Ubuntu 26.04.1 LTS**,hostname `User-2026QYRQXO` |
| 连法 | `ssh -i ~/.ssh/id_ed25519_verifyenv -o IdentitiesOnly=yes [email protected]` ⚠️ **`id_ed25519` 会被拒**(`Permission denied (publickey,password)`) |
| ⛔ 禁路 | `wsl.exe` **被沙箱 Program Blacklist 拦**(提示明确「不得重试/绕行」)⇒ **一律走 ssh** |
| 环境 | **node v22.23.2(与生产服务器同版本)** / python3 3.14.4 / 32 核 / 23.8 GB |
| 落地约定 | `tar czf - -C <插件包> . \| ssh … 'mkdir -p /root/sf-smoke-<HHMMSS> && tar xzf - -C …'` —— **每次新目录、不删旧**(照 PLAYBOOK §17「换新目录名别删」) |
| 冒烟入口 | 插件包内 **`scripts/smoke.mjs`**(2026-09-21 新增,**可复跑**;`package.json` 的 `files` 不含它 ⇒ **不进 tgz**) |
| 判据 | **Ubuntu 上必须 `34/34` 全绿**(退出码 0) |
📌 **2026-09-21 首次对照实测(同一脚本、同一产物)**:**Windows 32 过 / 2 败** ⟷ **Ubuntu 34 过 / 0 败**;败的恰是两条 ★ **大小写敏感**项(`/storyforge/INDEX.HTML` 与资产名全大写:Windows 返 **200**、Ubuntu 正确返 **404**)⇒ **这就是必须有 Linux 靶机的实证理由**:Windows 文件系统不区分大小写,会把这类缺陷判成绿的。
---
## §1 已落(只增不删,带证据)
- **2026-09-20 规划棒**:可行性方案 v2 出(形态 **候选 A 同源 iframe**;不 fork;关 SW;provider 收敛 deepseek;M1 入口 = 全屏/新标签页;锚点锁 `v3.9.1`)。落点 `E:\ProgramData\AI技能\dsh-plugin-forge\`。
- **2026-09-20 23:45 序1 执行棒(收官)**:
- 上游副本 `E:\github\storyforge` @ **`v3.9.1`** / sha **`7286497e`**(`clone` + `checkout` 均 rc=0)。
- **3 处上游改动全部落地**并**加严**:关 SW(删 `VitePWA` + 注册改 no-op + `index.html` 清理改全主机执行)|provider 收敛(18 条中仅 `deepseek` 启用,其余「待启用」)|删 dev 代理(**实测 12 条**,非单子写的 15 条)并改同源 `/storyforge/api/llm/<provider>/...`。
- `npm ci`(764 包 / rc=0,Node 22.22.2 **无 engine 报错**)→ `npm run build`(`tsc -b && vite build`,**rc=0**)。
- 验收六条全绿:`v3.9.1` | 产物内 `https?://api\.` 与 `127.0.0.1:` **0 命中** | `dist/index.html` OK | 静态冒烟 `/storyforge/`=**200**、`sw.js`=**404** | `node --check lib/client.js` rc=0 | `exports.default` **0 命中**。
- 插件包骨架落 `E:\ProgramData\AI技能\dsh-plugin-forge\dsh-plugin-storyforge\`(`package.json` / `cordis.patch.yml` / `lib/{index.js,client.js}` / `dist/` 110 文件 / `LICENSE`;总 6,510,823 B;`inject=[]`、`dshCompat=">=0.1.5 <0.2"`、version `0.1.0`)。
- 证据全文 = 单子 **§八**(含 6 条「失败尝试与假阴性」);单子另加 **§九「交给 序2/序3 的输入」**(宿主待补路由三张表)。
- ⚠️ **未做浏览器渲染验证**(本机无 Chromium)⇒ 只到 **L2(命令)+ L3(对账)**。
- 🔴 **单子口径修正 3 处**(已在单子内就地标注):① 「coreutils 全部缺失」**不成立**(`dirname`/`ls`/`mkdir`/`md5sum` 全在 `/bin`,`handoff-guard.sh` 实跑成功)② dev 代理 **12 条**而非 15 条 ③「不再出现 `-proxy`」按**代码级**口径达成(注释内说明性提及有意保留)。
- **2026-09-21 00:3x 序2 规划棒(立单完成)** —— 只读前置取证 + 出单,**未改插件包/上游/平台代码**。产物 = `dsh-server-docs/交接单/StoryForge插件线-序2-部署与实例验收.md`(8 段模板,`.lock-02` 已原子占号)+ `交接单/README.md §一` 新增序2 一行。⚠️ **当时仍按旧约定落文档库**;**2026-09-21 起本线文档改为只落本工作区**(本工作区已有同内容副本,含 §八 回填证据)。
- 🔴 **本棒决定性取证(序2 执行棒的必读前置)**:
- **路由 API 必须用 `kind`**:`E:\github\dsh-desktop-0.1.5rc1\packages\host\webserver\lib\index.js:176-178` 实测 `const table = route.kind === "exact" ? this.exact : this.prefixes;` ⇒ 既存插件(social-workbench / voxemw-cloud)写的 `exact: true` 在 0.1.5-rc.1 会落**前缀表**(语义漂移)⇒ 本包用 `kind:'prefix'`。
- **`registerFallback` 只有一座**且被官方 `@deepseek-ai/dsh-host-frontend-static` 占用 ⇒ SPA 回退**必须写在自注册 prefix handler 内**,否则注册即 throw ⇒ 整包加载失败。
- **回退只对「无扩展名」路径生效** ⇒ 带扩展名未命中一律 404,否则 `/storyforge/sw.js` 会回退成 HTML ⇒ SW 复活(M1 明令关闭)。
- **宿主半区 `inject` 声明方式**:模块级 `const inject = ['webServer']` + `export { apply, inject }`(social-workbench `lib/index.js:216,218` 实证);与 `package.json` 的 `dsh.client.inject`(管 client 半区)**是两回事**。
- **本序无需改平台 `src/`**:`src/supervisor/proxy.ts` 的 `targetPath` 默认 = `rawUrl`,**无路径白名单** ⇒ 未知前缀原样透传。
- **缓存判据**:代理给 `no-cache` 的条件 = 路径以 `/plugins/` 或 `/assets/` 开头 **或** `content-type` 含 `text/html`(`proxy.ts:487-491`)⇒ SPA 深链响应**必须**是 `text/html`,否则拿不到 `no-cache`。
- **投放链路**:`POST /api/plugins/business`(admin 会话,body `{file: base64, filename: *.tgz}`,同名整体替换,P0 命中默认 409 fail-closed)→ `POST /api/plugins/mine/apply`(`{plugins:[{id,enabled}]}`,**对已启用项是 noop** ⇒ 更新须「先停用再启用」)。admin 会话用 47 上 `node /opt/dshs/mksess.cjs`(**TTL 10 分钟,现建现用,用完删 `sessions` 行**)。
- **内存基线**:每实例 基础 **448 MiB** / 浮动上限 **1024 MiB**;本包 6.21 MiB 静态文件 ⇒ 宿主侧 RSS 增量 ≈ 0(每请求读盘、不缓存)。
- **2026-09-21 00:42 → 01:1x 序2 执行棒(部分收口 · 🔴 卡点已定性)** —— 单子 §五 S0→S7 走完,S0–S5 **全绿**,**S6 受阻**。证据全文 = 单子 **§八**(10 段已回填,含 8 条「失败尝试与假阴性」)。
- **S0**:全局执行锁 `mkdir` 成功(`OWNER` = 本会话)+ `.doing-StoryForge序2` 已占。
- **S1 前置 5 条全过**:`dsh 0.1.5-rc.1` / 域 `ai1net.com` | 🔴 **`kind` 决定性行实测在 `webserver/lib/index.js:177`**(单子写 176–178,**行号差 1**,内容逐字一致)| fallback 座位 `throw` 在 `:206`、占用方 `frontend-static/src/index.ts:124` | `inject` 声明法(social-workbench `:216/:218`)|包现状 `dist/` **110** / 总 **6,510,823 B** / `files` 字段**缺席**。**另证 `dist/sw.js` 不存在** ⇒ 判据 4 的 404 是真 404。
- **S2 实现(改 4 文件,回滚点存包外 `_backup-storyforge-xu2/`)**:`lib/index.js` 全文重写(`export const inject = ["webServer"]` + `ctx.effect(() => server.register({kind:'prefix', path:'/storyforge', handler}))`,**保留**原标记写入)|**新增** `lib/static.js`(纯函数 `planRequest` + 写响应 `serve`;**唯一语义差异 = `fallbackOnMiss`**:回退只对无扩展名路径生效)|`package.json` 加 `files` + 改过期 `description` |`cordis.patch.yml` **仅注释**(原写「无 inject」序2 后与事实相反)。
- **S2 验证 4/4 过**:`node --check` rc=0 ×3 | `exports.default` **0** | `kind:"prefix"` + `inject` 命中 | `files=['lib','dist','cordis.patch.yml','LICENSE']` / `0.1.0` / `client.inject=[]`。
- **S3 本机等价验收 = `4/4 PASS`**(**50 断言 0 失败、退出码 0**;真起 `node:http` + 调 `serve()`,非纯模拟):① `/storyforge/` = 200 `text/html;charset=utf-8` **4689 B 逐字节 == dist/index.html** ② hashed JS = 200 `text/javascript` **710117 B == 包内同名文件** ③ `/storyforge/settings` = 200 **含 `text/html`**(回退体逐字节 == index.html)④ `/storyforge/sw.js` = **404** 且体为空(未回退成 HTML);旁证:多级深链 200/html、POST 405、HEAD 200 无体、带扩展名未命中 404、越界非 200。
- **S4 打包**:`dsh-local-storyforge-0.1.0.tgz` = **1,754,963 B** / md5 **`dca573b57814de6c205b7be6d3772760`**;`package/dist/` = **110**(= 本机 dist 数);`package/{package.json,cordis.patch.yml,LICENSE}` + `lib/{index.js,static.js,client.js}` **6 条全中**;**无** `.bak`/`node_modules` 脏条目。
- **S5 候选池 ✅**:`POST /api/plugins/business` = **200**、`ok:true`、`replaced:false`、`trustedOverride:false`、🔴 **`blocked=0` ⇒ 未触发 409、未用 `trust` 放行**(单子 §四 预警的 vendor 假阳性**未出现**);`compat.level = unknown`(**非 `incompatible`**):`"插件未声明也未直接 import 任何 @deepseek-ai 包(扫描 106 个 js 文件)"`;18 条 `warnings` **全落 `package/dist/assets/**`**、`lib/**` 零命中。池内 `tgzPath = /var/lib/dshs/business-plugins/_dsh-local_storyforge.tgz`(**条目保留**)。
- **S6 实例启用 ⛔ 卡点**:靶 = admin 实例(uid **114801** / `w-47` / `dsh --profile web --port 20000`)。`mine/apply` 返 `taskId=397355a8a6d6fb78`,**终态 `failed`**(`安装失败,请重试或联系管理员`,93 s),阶段**恒停 `正在重启实例并探活…`**。**实装却成功**:`node_modules/@dsh-local/storyforge` 软链到位、`dist/index.html` md5 **`893e2034dd7e389e805b33310d051efb`** = 包内、属主全 **114801**、root 文件 **0**(R10 过)。平台随后 `restoreProfile` **自动回滚** ⇒ `bundles` 现**不含**本包。
- 🔴 **关键判据:插件不是肇事方(四条独立证据)**:① 实例内标记 `.dsh/.dsh-storyforge.log` 显示 **`route registered kind=prefix path=/storyforge` ×4**(`16:53:54 / 16:54:07 / 16:54:19 / 16:54:31`)⇒ 真实例内**加载成功、注册成功、dist 解析正确**;② `dsh_instances` 该行 `last_error=null` / `exit_code=null` / `epoch` 未变 / heartbeat 实时 / scope 持续 active ⇒ **从未崩溃**;③ **代码未走到「隔离/自动禁用」分支**(阶段无 `实例未能启动…`、无 `正在定位问题插件(1/1)`,`audit_log` 无 `plugin_incident`、无 `apply_business_plugins`)⇒ 是 `restartProbe()` **抛异常**,非探活判定为坏;④ 同平台 `2026-09-14T12:03:16Z` **有**该分支正常留痕的先例(`plugin_incident reason:"实例启动后崩溃"`)⇒ 本次无痕 = 未走到那里。
- ⚠️ **旁证(门户启用链路已长期不正常)**:`audit_log` 全表 `apply_business_plugins` **最后一条 = `2026-09-14T12:03:28Z`**;此后平台侧部署(含 09-20 MCN 线投放)实际走 **`ensure-biz-plugins.cjs` 直铺**(不写该审计)。
- ⚠️ **另记两条环境噪声(与本序无关)**:失败窗口 `guest.ai1net.com` 对真实浏览器返 **503**;日志大量 `[relay-dialer] 拨 ops/w-106:21000 失败:refused: busy`。
- ⛔ **未做(明示)**:**client 半区**(导航入口 + `<iframe src="/storyforge/">` 面板)本序 §一/§三/§六 均未纳入 ⇒ 目前「真机打开」只有**直达 URL** 一条路径。**真机判据 1–4 未验**。
- 🧹 **清理已做**:临时 admin 会话已删(`delete from sessions where user_agent='poc-curl2'`,1 → 0);47 上临时脚本已清;池条目保留。
- **2026-09-21 06:1x–06:4x 规划棒2(分诊 + 出跨线对接单)** —— 用户拍板走 **A**(平台侧修探活腿)。本棒**只读取证 + 出单**:⛔ 未改平台 `src/`、未改插件、**未写任何共享文件**。⚠️ 曾误把「文档要落文档库」当成前提,于是被 `guest实例503排障-20260921` 持的全局锁挡住;**用户 06:3x 纠正:本线文档落本工作区** ⇒ 与全局锁**无关**,该判断已回改(见 §2)。
- **失败链路已逐跳取证(10 跳,全部带 `file:line`)**,写进对接单 §三,省去平台会话重新摸索:
`business-plugins.ts:648-651`(`restartProbe` 两条 await **均未包裹**)→ `:665/676/691`(三处调用点)→ `:708-717`(兜底 catch)→ 🔴 `:543-551`(**`friendlyError` 把任何未知异常压成 `安装失败,请重试或联系管理员`**)→ `:718-732`(外层 catch 的 `console.error` **只在异常逃出内层时才打** ⇒ 本案例什么都没打);
下半段:`orchestrator.ts:532-566`(`restartAndProbe` 自身不抛,但 **:544 `restartMain` / :546 `spawnWatchdog` 未包裹**)+ 集群模式 `remote-spawner.ts:388-396`(`hostFor` + `call` ↔ 取址在 `:235`,注释原文「**`agentBaseUrlOf()` 现在会抛**」)+ `reachability.ts:99-105`(抛 `via=relay 但解析不出落点`)+ `remote-user-fs.ts:200-202`(`writeHandoff` → `POST /fs/handoff`)。
- **根因三候选(⛔ 明确写了"别凭候选表下结论,必须实测判定")**:① 取址抛错(**与档案 136 同族** ⇒ 若命中应**合并处理,不另起第二处判据**)|② HTTP 调用失败/超时(失败窗口内 `relay-dialer … refused: busy` 大量出现)|③ `restartMain`/`spawnWatchdog` 抛错。
- **⏱ 一条矛盾留作实测题**:整次耗时 **93 s**;候选 ① 是**同步抛**,应在安装后数秒内失败 ⇒ 93 s 更像候选 ②。**由 S2 实测回答,不许猜。**
- **🔴 关键发现(对接单 §二-2)**:失败路径**完全不落审计** —— `audit_log` 全表 `apply_business_plugins` **最后一条停在 `2026-09-14T12:03:28Z`** ⇒ 这也是「门户启用链路已长期不正常」的直接证据。
- **对接单要求平台侧先做的一步 = 让异常可见**(内层 catch 出声 + 落审计 + 原始 message 带进 `task.error`):没有这一步,**后面任何修复都无法验证**。
---
## §2 🎯 本轮动作
### ✅ 已收官:**规划棒2**(2026-09-21 06:1x–06:2x)—— 分诊 + 出跨线对接单
**用户 2026-09-21 拍板**:走 **A 方案**(平台侧修 `mine/apply` 的「重启+探活」腿),并要求「生成对接文档让平台会话评估」。
**产出**:`E:\ProgramData\AI技能\dsh-plugin-forge\对接单_平台侧-mine-apply重启探活腿异常_20260921.md`(8 段)
- 含**已取证的代码地图 10 跳**(`file:line`,见 §1 末条),省去平台会话重新摸索;
- 含**根因三候选 + 区分方法**(⛔ 明确写了「别凭候选表下结论,必须 S2 实测判定」);
- 含**必须先做的一步**:让异常可见(内层 catch 现在把真因压成一句通用文案,且不落审计);
- 含验收 5 条 + **R8 影响面**(修这条腿通常要 `systemctl restart dshs` ⇒ 会中断在线用户,须先知会)。
- ✅ **落点 = 本工作区** `E:\ProgramData\AI技能\dsh-plugin-forge\`(用户 2026-09-21 约定:**本线文档一律落本工作区**)⇒ **无需**写 `dsh-server-docs`、**无需**抢任何锁。
> ⚠️ 本条**替代**此前「未落文档库 ⇒ 待锁释放后补」的写法 —— 那是**错误套用了「单子落文档库」的旧约定**,把本可零冲突的写作挡在别人的锁外面。平台会话按**绝对路径**读本单位即可。
### ⏭️ 本线下一项(⛔ 本棒**不预登记**)
| 序 | 内容 | 前置 |
|---|---|---|
| **0** | ✅ **已完成**(2026-09-21 06:5x):平台项目内本线文档已清零 —— 删 2 份单子(先备份)+ 摘 `交接单/README.md §一` 本线 2 行(46,215 → 40,480 B)+ 用官方脚本刷新 `docs-manifest.json`(StoryForge 命中 4 → **0**);`docs-audit.py` **RC=0 无 P0** | 回滚点 = 本工作区 `_backup_docs_revert_20260921/`(4 件)。⚠️ 两个残留:① 服务器镜像 `/opt/dsh/docs` **未 scp**(推送需用户明示)② carbon 单里两处提及本线 —— 属**别人的文件**,未动 |
| **2-补** | **序2 补跑 S6**:在 w-47 的 admin 实例(uid `114801`)上补齐真机验收 1–4 | 🔴 **等平台侧修好探活腿 + 回执**;修好前登记也无用(撞同一堵墙) |
| **2b** | **client 半区**:导航入口 + `<iframe src="/storyforge/">` 面板(序2 §一/§三/§六 均未纳入 ⇒ 目前真机打开只有直达 URL 一条路径) | ✅ **无前置、无拍板项** ⇒ 可独立开工 |
| **3** | LLM 代理与配额(`/storyforge/api/llm/*`) | 🔴 **含拍板项**(模型 KEY 来源 / 每用户配额 / 是否由平台承担调用成本)⇒ 等用户拍板;且须先解 S6 |
| **修-a** | **`lib/client.js` 头注释过时**:现写「导航入口与 `<iframe src="/storyforge/">` 面板属**序2**」,但**序2 明确未纳入** ⇒ 会误导下一棒(文件本身仍是 1,630 B 骨架,`inject=[]`) | ✅ 无前置、无拍板项,**本线自决** |
| **修-b** | **`scripts/smoke.mjs` 在 Windows 上退出码 1**(两条 ★ 大小写项必然失败)⇒ 容易被误判成"产物坏了"。改法:把 ★ 平台相关项在**非 Linux** 上判 **SKIP**(不计失败),只在 Linux 计分并保留严格退出码 | ✅ 无前置、无拍板项,**本线自决** |
| **纪律** | 序2b / 序3 改过 `lib/` 后重新 `npm pack` **必须升版本号**(pnpm 按版本复用同名 tgz,同号覆盖重发不安全 —— PLAYBOOK §9.3;候选池现为 `0.1.0`) | 无 |
### ⛔ 本棒未登记下一棒的原因(两条都命中 ⇒ 按登记门禁不登记)
① 下一棒(2-补 / 3)均被**外部窗口**阻塞(等平台修好 / 等用户拍板);
② 收口时**仍有别的会话在跑**(`guest实例503排障-20260921` 自 06:16 持全局锁)。
⇒ **链条暂停**:等平台回执或用户拍板后,再由**当时的会话**立棒。⛔ 不预登记队列。
### ✅ 上一棒:**序2 执行棒**(2026-09-21 00:42 → 01:1x)部分收口
见 §1 末条 + 单子 §八(10 段已回填,含 8 条「失败尝试与假阴性」)。**S0–S5 全绿**;**S6 受阻**(平台探活腿)⇒ 真机判据 1–4 **未验**、实例当前**未启用**。⛔ 未改平台 `src/`、未改上游、未 commit / 未 push。
> 📌 **A/B/C 三候选的完整优点/缺点**已随用户 2026-09-21 的拍板归档(选 **A**);三候选原文见本工作区 `.workbuddy/memory/2026-09-21.md` 与 `交接单_StoryForge插件线-序2_部署与实例验收_20260921.md`。⛔ 本段不再保留副本,避免第二真相源。
### 冷启动备忘(每一棒照做)
1. **开机第 0 步**:`export PATH="/bin:/usr/bin:$PATH"` —— **必须**,否则 `dirname`/`ls`/`mkdir`/`md5sum` 全报 `command not found`(真因是 `/bin` 不在 PATH,**不是 coreutils 缺失**,2026-09-21 自证)。
⛔ **不抢全局执行锁**:本线文档与产物**全部落本工作区**,那些文件**没有第二方会碰** ⇒ **没有锁的对手方**;`dsh-server-docs/交接单/.exec-lock` 对本线**不适用**(用户 2026-09-21 三次纠正,判据已固化进 `MEMORY.md §六`)。本线唯一的"锁"是**排期铁律:同一时刻只挂一个接续棒**(见第 2 条)—— 它治的是"本线自己别开两个会话重复开工",**不是跨会话争抢**。
🔴 **只有要动共享资源时**才需要那把锁,共三类:① 写 `dsh-server-docs`(本线**默认不做**)② 改平台代码 `D:\github\dsh_shenxian\src` ③ 服务器操作(scp / 重启 / 铺插件)。那时:`bash "D:/github/dsh_shenxian/dsh-server-docs/scripts/handoff-guard.sh" --claim-exec "<会话名>"`,抢不到 ⇒ **只报告 + 立刻停**(R9:⛔ 不删锁、不接管)。⚠️ `.exec-lock` 是**目录**(内含 `OWNER` 文本),**不是文件**。
2. 收尾四件套:单子 §八 回填 → 本文件 §2 推进到下一序 → 追加当日 memory 小节(⛔ 不重写别人段落)→ 登记下一棒(`scheduledAt` = 收口 + 5~8 分钟,**同一时刻只挂一个**;含拍板项或被外部窗口阻塞则改为告知「链条已暂停」)。
⚠️ **本线默认不持锁 ⇒ 收尾没有"释放锁"这一步**;仅当本次真的动过共享资源(第 1 条那三类)才 `--release-exec`。