Author SHA1 Message Date
johnQ dde146995e feat(req): 新增「复用盘点」门禁(Gate 5 + PRD §1.5)
- req 技能:4 道门禁 → 5 道,新增 Gate 5「复用盘点先于估工」;无 file:line 证据或标「未排查」禁止提审、禁止按全新增估工
- req-prd 模板:新增 §1.5「现有实现与复用盘点」强制章节(能力点 / file:line / 复用|改造|新增 / 理由)
- 触发:DS Bot 已有头像上传却被当新增估工的事故复盘
- 版本:req-plugin 2.0.0 → 2.1.0,req-prd-plugin 2.2.0 → 2.3.0;marketplace.json 同步再生成
2026-10-27 11:03:47 +08:00
johnQ d5550d62c5 docs(slark-dsh-cicd): 修正参考文件错别字(REQ-20261007-0003) 2026-10-07 17:07:15 +08:00
johnQ c74fe71f0a feat(slark-dsh-cicd): 新增 DSH/Slark 发布实战技能(REQ-20261007-0003)
- 新建 skills-dev/slark-dsh-cicd-plugin:plugin.json + skills/SKILL.md +
  skills/references/fork-release-and-post-checks.md
- 覆盖两条发布线(DSH 插件 pack 双资产 / Slark 宿主重打包+签名)、权限与身份三连
  (push:true 才可写,ls-remote 通 ≠ 能 push)、无上游写权限的 fork 发布且原仓库零改动、
  干净单提交 + git diff --stat <OURS> 树一致性 gate、改指向后必须重新 pack、
  draft→publish 与「更新检查看不到新版」的因果、GitHub HTTPS/fetch 卡死/无 macOS timeout
  的判死处置、发布后 SHA 逐项核对(verify-remote 仅 Windows)、app.asar 完整性约束、
  ai-proj staging→released 与 /req done 证据要求及文档接口故障绕过
- 与 slark-cicd 明确分工并交叉引用,不重复 staging/生产/Desktop 细节
- update-plugin-meta.py PLUGIN_MAP 增加 slark-dsh-cicd-plugin (skill/dev)
- generate-marketplace.py devops 分类补充 slark-dsh-cicd;marketplace.json 仅新增该条目
- 校验:claude plugin validate . 通过(警告为该仓库既有 install_name 字段提示)、git diff --check 干净
2026-10-07 17:06:36 +08:00
johnQ 2e0f7fe68c feat(dev-coding): 新增「Step 0.5 Shell 环境自检」— node/npm 缺 PATH + HOME 未设置
在 macOS + Slark/DSH 实测定位到:bash 工具以 bash -c(非登录非交互)执行,
子进程环境等于宿主进程环境(PATH 不被清洗,HOME/USER/TMPDIR/LANG 未设置),
宿主由 GUI 应用拉起继承最小 PATH → node/npm/git 直接不可用。

沉淀内容:
- 5 条根因(不读 rc / PATH 不被清洗 / GUI 宿主 / HOME 未设置 / shell-env 只认 DSH_*)
- 两行 export 模板:HOME + node v22.22.3 PATH(勿混用 homebrew 的 v26)
- HOME 必须补的理由:~ 靠 passwd 展开易误判,git 报 fatal: $HOME not set,
  set -u 脚本报 unbound variable;「node 能跑」≠ 环境完整
- 交付纪律:禁止把「没有 shell / 不能构建」写成交付结论,未跑构建就如实标注
2026-10-07 07:42:00 +08:00
qiudl 3ffee89840 Merge pull request '[REQ-20260825-0006] Harden Slark handoff recovery guidance' (#10) 2026-08-25 21:15:44 +00:00
qiudl 998b22e905 fix(slark-cicd): document handoff recovery
Refs REQ-20260825-0006
2026-08-26 06:33:14 +09:30
qiudl 826bba8485 Merge pull request '[REQ-20260824-0017] Add req-dev read-model migration gate' (#9) from docs/req-dev-read-model-migration-gate into main 2026-08-25 19:57:07 +00:00
qiudl f5c5bd3f40 docs(req-dev): add read-model migration gate 2026-08-26 05:24:36 +09:30
qiudl 4e6ae9036a fix(installer): detect same-metadata content updates 2026-08-22 08:53:46 +09:30
17 changed files with 614 additions and 13 deletions
+15 -2
View File
@@ -405,6 +405,19 @@
"name": "slark-cicd-plugin",
"source": "./skills-dev/slark-cicd-plugin",
"description": "Slark 仓库 staging、生产与 Desktop 安装包的端到端 CI/CD 发布技能。",
"version": "1.1.0",
"category": "devops",
"keywords": [
"devops",
"deployment",
"operations"
],
"strict": false
},
{
"name": "slark-dsh-cicd-plugin",
"source": "./skills-dev/slark-dsh-cicd-plugin",
"description": "DSH 插件包与 Slark 宿主补丁的发布实战技能:npm run pack 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、draft Release、发布后 SHA 核对、asar 完整性与 ai-proj 状态对齐。",
"version": "1.0.0",
"category": "devops",
"keywords": [
@@ -483,7 +496,7 @@
"name": "req-plugin",
"source": "./skills-req/req-plugin",
"description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。",
"version": "2.0.0",
"version": "2.1.0",
"category": "productivity",
"keywords": [
"project-management",
@@ -496,7 +509,7 @@
"name": "req-prd-plugin",
"source": "./skills-req/req-prd-plugin",
"description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。",
"version": "2.2.0",
"version": "2.3.0",
"category": "productivity",
"keywords": [
"project-management",
+1 -1
View File
@@ -60,7 +60,7 @@ def load_config():
def get_category_and_keywords(plugin_name):
if any(x in plugin_name for x in ['dev-', 'coding', 'frontend']):
return "development", ["development", "coding", "workflow"]
elif any(x in plugin_name for x in ['ops-', 'deploy', 'server', 'slark-cicd']):
elif any(x in plugin_name for x in ['ops-', 'deploy', 'server', 'slark-cicd', 'slark-dsh-cicd']):
return "devops", ["devops", "deployment", "operations"]
elif any(x in plugin_name for x in ['ai-proj', 'req']):
return "productivity", ["project-management", "tasks", "requirements"]
+1 -1
View File
@@ -365,7 +365,7 @@ install_plugin() {
else
mkdir -p "$dst_dir"
# rsync resolved source (handles nested skills/ structures)
rsync -a --delete "$src_dir/" "$dst_dir/"
rsync -a --checksum --delete "$src_dir/" "$dst_dir/"
state_set "$install_name" "$version" "$effective_install_type" "$source_digest"
ok "$install_name → skill (v$version)"
INSTALL_ACTION=true
@@ -608,3 +608,55 @@ fi
### 非阻断原则
架构信息缺失时**仅警告不阻止**。用户仍可继续,但会被告知"生成的建议可能不够准确"。
---
## Step 0.5:Shell 环境自检(node/npm command not found 先看这里)
**在任何 `npm` / `pnpm` / `node` 命令之前,先确认解释器可解析。** 报 `command not found` 时
**不要**下结论"当前环境没有 shell",也**不要**去找"给 agent 配 shell"的开关(DSH 没有这个开关)。
### 根因(不要重复侦察;已在 macOS + Slark/DSH 上定位到代码)
| # | 事实 |
|---|------|
| 1 | DSH 的 bash 工具(`@deepseek-ai/dsh-bash-local`)一律以 **`bash -c`(非登录、非交互)** 执行 → **不读** `~/.zprofile` / `~/.zshrc` / `~/.profile` |
| 2 | 子进程环境 = **宿主进程环境** − 凭据类变量 − `DSH_*`(`@deepseek-ai/dsh-subprocess`)→ **PATH 不被清洗**,它等于启动宿主那个进程的 PATH |
| 3 | 宿主由 GUI 应用(Slark.app)拉起 → 继承 macOS GUI 的最小 PATH(`/usr/local/bin:/bin:/usr/bin:...`)→ node 不在里面 |
| 4 | `HOME` **未设置**(不是空值);`USER` / `TMPDIR` / `LANG` 同样未设置 |
| 5 | `@deepseek-ai/dsh-shell-env` 只注册/注入 **`DSH_*`** 变量,`PATH` 是保留键 → 改 profile/`cordis.patch.yml` 加 PATH **无效** |
### 正确做法(两行 export,别多一步)
```bash
export HOME="/Users/johnq" # 必须补:git 等直接读 $HOME
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:$PATH" # node/npm/pnpm
cd <项目> && npm install && npm run build
```
**为什么 `HOME` 必须补**(实测):
- `~` 在 bash 里靠 passwd 查表展开,所以 `ls ~`、`cd ~/x` 仍能用 → **容易误判环境正常**;
- 直接读 `$HOME` 的会崩:`git` → `fatal: $HOME not set`(`status` / `commit` / `config --global` 全废);
带 `set -u` 的脚本 → `HOME: unbound variable`(例:ai-proj-helper 的 `install-skills.sh`);
- node 侧多数无事(`os.homedir()` 走 passwd 兜底,只有 `process.env.HOME` 是 `undefined`)
→ **「node 能跑」≠ 环境完整**,别据此跳过 `HOME`。
- **本机 node 必须用这一支**(定义在 `~/.zprofile`,对齐项目 `engines.node`):
`/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin/node` = v22.22.3,npm 10.9.8。
- `/opt/homebrew/bin/node` 是**另一支 v26**,不要混用;`/usr/local/bin/node` 不存在。
- 备选①:`/bin/zsh -lic '<命令>'`(登录 zsh 会读 `~/.zprofile`)。
- 备选②:绝对路径直调 `/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin/node build.mjs`。
- 路径变了先 `ls -d /Users/johnq/.local/node-v*-darwin-arm64/bin` 重新取**真实版本号**,不要猜。
### 交付纪律(配合「Step 0:验证优先」)
- **禁止**把"没有 shell / 不能构建"写成交付结论:bash 工具是好的,缺的只是 `HOME` + `PATH`。
正确表述是"我未执行构建(缺 node PATH,已给用户补 PATH 后的构建命令)"。
- 构建/测试没跑就说没跑(`未经构建与实机验证`),**不要**用"构建配置已就绪"之类中间信号冒充验收。
- 校验环境的最快方式(输出直接贴进交付):
```bash
export HOME="/Users/johnq"; export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:$PATH"
node -v && npm -v && git status --short | head -3
```
@@ -1,7 +1,7 @@
{
"name": "slark-cicd-plugin",
"description": "Slark 仓库 staging、生产与 Desktop 安装包的端到端 CI/CD 发布技能。",
"version": "1.0.0",
"version": "1.1.0",
"author": {
"name": "qiudl"
},
+36 -4
View File
@@ -1,6 +1,6 @@
---
name: slark-cicd
description: Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/CD 可执行 runbook。覆盖本地预推快检、PR 与内网 ci/internal-gate 门禁、合并 origin/main、staging 真机预验、审批式生产发布,以及在 m5max 构建签名、公证并上传 macOS/Windows Desktop 包到 OSS。当用户要在 slark(qiudl/qiu-slark)里发布/上线、部署到生产或 staging、发布 Desktop 安装包、盯 CI/合并 PR、复现或验证运行时行为、或排查发布链/推送路由问题时使用。
description: Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/CD 可执行 runbook。覆盖本地预推快检、PR 与内网 ci/internal-gate 门禁、合并 origin/main、生产前强制 staging 验收、审批式生产发布,以及在 m5max 构建签名、公证并上传 macOS/Windows Desktop 包到 OSS。当用户要在 slark(qiudl/qiu-slark)里发布/上线、部署到生产或 staging、发布 Desktop 安装包、盯 CI/合并 PR、复现或验证运行时行为、或排查发布链/推送路由问题时使用。
---
# Slark CI/CD runbook
@@ -17,6 +17,9 @@ description: Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/C
- 生产发布只接受**最新 `origin/main` 的精确 40 位 SHA**;`HEAD != origin/main` 会被 `deploy.sh` 拒绝。
- 生产发布必须在**普通 clone** 中执行,且 `.git` 必须是目录;`release-prod.sh` / `deploy.sh` 会拒绝
`.git` 为指针文件的 git worktree。不要污染现有 checkout:主目录脏或被占用时,新建干净的临时 clone。
- **生产发布前必须先发布并验收 staging,不得跳过**:同一最新 `origin/main` 精确 SHA 必须先完成迁移演练、
staging 正式部署、隔离检查和需求功能验收。把证据报告给用户后,必须再次取得明确的生产确认;开始 staging
前获得的生产授权不能替代这次确认。未通过、未完成或用户未确认时停止,不得执行生产发布命令。
- 审批式部署,永不自动:亲手敲的 `sudo`/`rm -rf`/`dd`/`curl|sh`/包安装是 `destructive`(NEVER_AUTO),
**每次都要人审**;命中审批闸就**等人批**,绝不绕过、绝不把这些 key 加进 auto-approve 白名单。
- staging 与生产硬隔离:staging 脚本带生产 IP 拒运行护栏、不 push origin、不反向 rsync。
@@ -31,7 +34,7 @@ description: Slark 仓库 staging、生产和 Desktop 安装包的端到端 CI/C
## 选择链路
- 「发布/上线/部署生产」→ 生产发布链:阶段 1→2→4→5。
- 「发布/上线/部署生产」→ 生产发布链:阶段 1→2→3(同 SHA staging 验收)→ 人工确认 →4→5。
- 「Desktop/桌面版打包、发布、上传 OSS」→ 阶段 1→2 后走 `docs/desktop-oss-release.md` 的 m5max 双平台链路。
- 「盯 CI/合并 PR」→ 阶段 1→2(用 babysit 心态循环到 green + mergeable + 评论收口)。
- 「复现/验证运行时行为、别碰生产」→ staging 预验链(阶段 3,可独立随时跑)。
@@ -70,7 +73,12 @@ git push origin --delete <feature-branch> # 删远端
git branch -D <feature-branch> # 删本地(先切走)
```
## 阶段 3:staging 真机预验(可选,随时)
## 阶段 3:staging 真机预验(生产前强制,也可独立运行)
生产候选必须使用最新 `origin/main` 的同一精确 SHA。先执行迁移演练,再正式部署;随后完成隔离检查和本次需求的
功能验收。验收不能只看通用 health:凡需求涉及真实集成(如飞书 OAuth),staging 缺少对应 provider/凭据时必须
明确标为未完成并停止生产链,除非用户在看到该限制后明确接受。完成后报告目标实例、SHA、Server/Daemon revision、
迁移演练、健康、隔离和功能结果,并等待用户再次确认是否进入生产。
### 3.1 当前权威路径:Slark 独立 staging
@@ -125,6 +133,9 @@ routed-remote 要 `network=enabled` 必须同时满足:① agent `workspace_ac
## 阶段 4:生产发布(审批式)
**前置门禁**:阶段 3 已对同一 SHA 完成 staging 部署和验收,证据已报告,且用户在报告之后明确确认生产发布。
缺任一项即停止;不得把 PR 合并授权、较早的生产授权或单纯的“继续”视为验收后确认。
**唯一姿势**:从干净的普通 clone 发布最新 `origin/main`(`HEAD == origin/main` 精确 SHA)。staging 可用
独立 worktree,但生产脚本为保证同步与发布边界,会拒绝 worktree;不要在用户已有脏 checkout 中清理或发布。
@@ -147,6 +158,17 @@ pnpm release:prod # = release-prod.sh → deploy.sh --backend --no-in
- 命中审批闸(发布/sudo)→ **等人批**;不要绕。脚本内部 SSH 载荷子进程的 sudo 不触发闸。
- 普通工作 clone 发布完切回原分支:`git checkout <branch>`;一次性发布 clone 可保留作审计,清理时用可恢复方式。
### HANDOFF_BLOCKED 恢复入口
发布准备遇到 `HANDOFF_BLOCKED`、handoff manifest=`failed_closed` 或命令状态不确定时,停止发布切换;禁止靠重跑
发布脚本、直接改表或默认 replay 来“解卡”。先把 manifest、命令、execution、lease/outbox 和审计证据做只读盘点,
再让用户明确授权每条记录的 reconciliation action/outcome 及是否允许继续发布。恢复细节见
[references/production-handoff-recovery.md](references/production-handoff-recovery.md);仅遇到该类事故时读取。
`COMMAND_AUTHORITY_CHANGED` 是有效并发护栏,不是可忽略错误:重新读取当前 authority/transport/execution generation,
确认期间没有新活动,再在原授权范围内幂等续办。若发现新工作、活跃 lease/outbox 或无法解释的副作用,立即停止并
重新请求决策。
## 阶段 5:发布后核查
```bash
@@ -154,7 +176,12 @@ scripts/verify-deploy.sh # 健康(前端/api/health=200)、bundle、server/da
```
预期 `✓ deploy verification passed: server=<sha> daemon=<sha>` 且 `<sha> == origin/main`,舰队 `online ≥ 发布前`。
镜像落后告警(默认 `--no-mirror`)非阻断,需补推按告警的幂等命令(需 Bitwarden session);镜像仅单向 fast-forward。
完成前还要独立复核 governed release 账本为 `succeeded/complete`、四组件实际 SHA 均为目标 SHA、租约已释放、
新 handoff manifest=`verified`;若本次处理过旧 manifest,还要核对其全部 case 已达授权终态且 replay 数为 0。
默认生产包装器使用 `--no-mirror`:构建 daemon candidate binary 用于运行态验证不等于上传镜像资产。镜像落后告警
在该模式下是预期的非阻断结果;不得未经单独授权补传镜像。交付说明要明确国内安装器可能回退 upstream,避免把
本地构建、生产 daemon 更新和镜像上传混为一谈。
## Desktop m5max 双平台发布
@@ -194,6 +221,11 @@ scripts/git-release-governance.sh status
| cohost Server 已更新但 daemon 仍是旧 SHA | 用 `--profile daemon up -d daemon-staging` 更新 daemon;核对两容器 revision 后才算完成 |
| cohost daemon 启动即退出/fatal | 看 `docker logs slark_daemon_staging`;修复凭据/环境或版本契约,禁止用旧 daemon 冒充验收通过 |
| verify 失败 SHA 不符 | 部署到了旧码,重发 |
| `HANDOFF_BLOCKED` / manifest=`failed_closed` | 停止切换并读取 handoff recovery 参考;只读盘点后取得逐项 action/outcome 授权,禁止默认 replay |
| reconciliation 返回 `COMMAND_AUTHORITY_CHANGED` | 查当前 generation 与新活动;无新活动才在原授权内幂等续办,有变化则停止重新决策 |
| `--no-mirror` 后提示镜像落后 | 预期非阻断;报告 upstream fallback,未经单独授权不上传镜像 |
| 生产授权已给但 staging 尚未验收 | 先完成同 SHA staging 全量验收,报告证据并重新等用户确认;不得沿用较早授权 |
| staging 缺少需求依赖的真实集成配置 | 将对应验收标为未完成并停止生产链;补齐配置后重验,或让用户基于明确限制作决定 |
## ai-proj 流程(强制)
@@ -0,0 +1,70 @@
# Production handoff recovery
仅在生产发布被 coding-session handoff 阻塞、manifest 进入 `failed_closed`,或存在 `needs_review` / 不确定命令时读取。
目标是保留 fail-closed 语义,在明确授权下终结歧义状态,并证明没有误重放命令。
## 先冻结发布,不先解锁
- 停止发布切换和自动重试;记录 release ID、候选 SHA、manifest ID、阻塞发生阶段与原始错误。
- 不把“继续”“再试一次”解释为 replay、force-end 或生产发布授权。向用户展示证据后,确认语句应绑定 manifest、
条目数量或 command ID、目标 action/outcome、是否禁止 replay,以及要继续发布的精确 SHA。
- 不直接更新 handoff、command、execution 表来改变状态。优先走已部署版本的内部治理 API;只有 API 无法恢复且
已获得对应操作授权时,才可调用该版本现有 domain repository,并保留 lifecycle event 与 audit log。
## 只读证据盘点
对 manifest 中每条 item 核对并保存摘要:
- command ID、session/project/computer ID、command status、execution state;
- manifest 记录的 authority term、transport generation、execution generation,以及当前实际值;
- command/execution lease 是否过期,outbox 是否仍有 active/pending 记录;
- session 中是否还有其他活跃命令或新事件;
- daemon acknowledgement、result/receipt、审计事件是否能证明执行或未执行。
若 lease/outbox 仍活跃、存在新命令、证据相互矛盾,或无法排除外部副作用,停止恢复并请求新的人工判断。
## Action 与 outcome 必须匹配证据
- `no_action / not_executed`:只有权威证据能证明命令从未执行,且治理接口允许该组合时使用。
- `terminate / terminated`:用于显式受控终结仍处于 uncertain/active 语义的命令;先通过 domain 操作取消 command、
force-end execution、完成 session 并写审计,再完成 reconciliation,不产生 replay。
- `mark_failed / failed`:证据确认失败、且不能安全恢复为成功或未执行时使用。
- `replay / replayed_success`:仅在用户明确授权 replay 且 exactly-once/幂等边界得到证明时使用。发布解卡绝不能默认 replay。
不要为了迎合预先选择的 outcome 忽略现场证据;如果授权与可证明事实不兼容,停止并把差异报告给用户。
## 受治理恢复顺序
1. 使用精确 manifest/item 建立或读取 reconciliation case。
2. 取得具备 action、evidence digest、operator 和有效期约束的审批。
3. 记录 decision;执行 domain 终结或治理 apply;随后用权威运行态证据 confirm terminal outcome。
4. 若 apply 返回 `COMMAND_AUTHORITY_CHANGED`,不要覆盖或绕过。重新读取当前三类 generation 和新活动:
- generation 只因刚完成的受控终结而变化,且无新命令、lease/outbox 时,可在同一授权 action 下幂等续办;
- generation 对应新工作或原因不明时,停止并重新审批。
5. 每个 item 都 terminal 后,确认 manifest 自动或受治理地进入 `reconciled`,再启动新的发布尝试。
凭据失败发生在事务前时应视为零变更并记录。不要反复猜测 owner 凭据;不得打印连接串或密钥。若必须使用运行时
`slark_app` 连接,只能通过已部署 domain repository、正确的 per-transaction project/user RLS context 和审计路径操作。
## 零重放与发布恢复验收
恢复完成至少证明:
- `terminalCount == totalCount`,每条 decision action 与 terminal outcome 等于用户授权;
- 禁止 replay 的场景下,decision action=`replay` 数和 execution replay attempt 增量均为 0;
- `terminate/terminated` 场景下,command=`cancelled`、execution=`force_ended`,并存在对应 lifecycle/audit 证据;
- 旧 manifest=`reconciled`,没有 active handoff reservation 残留。
继续生产发布时使用新 governed release / handoff manifest,不复用 failed-closed manifest。重新检查候选仍是最新
`origin/main`、同 SHA staging eligibility receipt 仍有效、工作树干净;任何一项变化都回到 staging 与人工确认门禁。
发布成功后再独立执行一次 `scripts/verify-deploy.sh`,并只读核对:release=`succeeded/complete`、target/actual Web、
Server、daemon、artifact SHA 全相等、lease 已释放、finished 已记录、新 manifest=`verified`、舰队在线数不低于基线。
将旧 manifest reconciliation 与新发布证据一起回写 ai-proj。
## `--no-mirror` 的解释边界
- `pnpm release:prod` 默认走 `deploy.sh --backend --no-install --no-mirror`;不上传 daemon 镜像资产。
- 发布过程仍可能在隔离 candidate 目录构建 daemon binary,用于同 SHA 验证或部署运行态 daemon;这不是镜像上传。
- mirror freshness 告警表示镜像源仍旧,国内安装器可能回退到较慢 upstream。它不阻断当前生产运行态发布。
- 补传 mirror 是单独的外部写操作,需要单独授权和凭据;不能为了消除告警在发布收尾时顺手执行。
@@ -0,0 +1,11 @@
{
"name": "slark-dsh-cicd-plugin",
"description": "DSH 插件包与 Slark 宿主补丁的发布实战技能:npm run pack 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、draft Release、发布后 SHA 核对、asar 完整性与 ai-proj 状态对齐。",
"version": "1.0.0",
"author": {
"name": "qiudl"
},
"install_name": "slark-dsh-cicd",
"install_type": "skill",
"dir_category": "dev"
}
@@ -0,0 +1,213 @@
---
name: slark-dsh-cicd
description: DSH 插件包与 Slark 宿主补丁的发布 runbook。覆盖 npm run pack 双 tgz 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、改指向后必须重新打包、draft→publish 节奏、GitHub HTTPS/SSH 与 fetch 卡死处置、发布后 SHA 核对、app.asar 完整性约束、ai-proj 发布状态对齐。当用户要发布或升级 DSH 插件(dsh-worktable 等)、发布/更新 Slark 宿主补丁、push 或下载 GitHub 失败、插件「检查更新」看不到新版本、需要 fork 发布、或排查 asar 完整性/签名问题时使用。
---
# DSH 插件与 Slark 宿主发布 runbook (slark-dsh-cicd)
面向「把插件包/宿主补丁真正发出去」的可执行清单。默认在**发布机**执行;命令按阶段照做,
每阶段结束先核验再进入下一阶段。
## 适用与分工
两条发布线,**互不依赖**,不要串流程:
| 发布线 | 产物 | 入口 | 权威源 |
| --- | --- | --- | --- |
| A · DSH 插件 | `dist/v<ver>/dsh-worktable.tgz` + `dsh-worktable-<ver>.tgz`(同一验收包) | `<pkg>/npm run pack` | GitHub Release(tag = `v<包内版本>`) |
| B · Slark 宿主补丁 | 重打包 + 签名的 Slark.app(含内嵌 DSH runtime) | 宿主自身打包/签名链 | 宿主发布链,不经 GitHub Release |
与同市场技能的分工,避免重复:
- **本技能**:DSH 插件包发布、宿主补丁打包侧的完整性约束、发布链本身(权限、tag、Release、SHA 核对)。
- `slark-cicd`:Slark 仓库 staging 验收、生产发布、Desktop 安装包上传 OSS、PR/CI 门禁——服务器与包分发细节以它为准。
- `dev-cicd`:通用流水线设计与排查(Gitea Actions/缓存/并发)。
- `ai-proj-cicd-release`:AI-Proj 服务自身的门禁与回滚。
## 铁律(先记死)
1. **打包只走唯一入口**:`npm run pack`(或仓库自带的等价脚本)。手写 `tar`/`npm pack` 会绕过结构断言与安装导入验证。
脚本自己会做:版本一致性 → 兼容回归 → 构建 → 语法检查 → 结构断言(精确文件清单、无 `src/`、无 `.map`)→
独立临时目录真实安装 + `import()` 断言 → 客户端/宿主门禁 → 双资产同源 → SHA-256。
2. **`package.json` 与 `dsh.plugin.json` 的版本必须相等**,否则构建与打包脚本 fail-closed。发版只升这一处对,不要只改一边。
3. **产物出了包就不再改**:任何源码/常量/文档改动都可能改变 bundle → 旧 `.tgz` 立即作废、sha 必变。**改完必须重新 pack 并重新记录 sha**。
4. **对上游仓库零改动是硬指标**(尤其是没有写权限的外部作者仓库):不 push、不打 tag、不建 Release/PR,一次都不做。
5. **公开发布必须是 draft 起步**,对外动作经用户过目后再 `--draft=false`。
6. **force push 只允许发生在自己 fork 的分支/tag 上**;绝不对上游使用 `-f`。
7. **发布用的分支必须是干净单提交**,不要为了省事把本地历史推上去(本地历史可能含个人路径、凭据、临时调试 commit)。
8. **"下载通了" ≠ "hash 对了"**:发布完成的判据是「远端资产 sha == 本地验收 sha」,不是页面能打开。
## 阶段 0:身份与权限三连(先判定能力,再决定链路)
```bash
gh auth status # 登录账号是否符合预期
ssh -T git@github.com 2>&1 | head -3 # SSH 身份(回显 Hi <user>! 即通)
gh api repos/<owner>/<repo> --jq '.permissions' # 关键:{"pull":true,"push":<bool>}
```
- **只有 `push:true` 才能走「上游直发」**;`push:false` 时不要硬撞,直接走阶段 2 的 fork 发布。
- **`git ls-remote` 通 ≠ 能 push**:读权限随时可通,写权限被拒要到 push 才暴露。不要用 ls-remote 成功推断可写。
- 同时确认远端协议:`git remote -v`。HTTPS 到 GitHub 可能整体不通(见「网络与链路故障处置」),
必要时改用 SSH:`git remote set-url origin git@github.com:<owner>/<repo>.git`。
## 阶段 1:发布线 A — DSH 插件(有上游写权限)
```bash
export HOME="/Users/johnq"
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:/opt/homebrew/bin:$PATH"
cd <repo>/<pkg>
npm run check && npm run test:gate && npm run pack
shasum -a 256 dist/v<ver>/*.tgz # 两个文件必须同 sha
tar -xzf dist/v<ver>/dsh-worktable.tgz -C "$(mktemp -d)" && grep -rn "<旧仓库名>" <解包目录> # 期望无输出
git add -A && git commit -m "release: <name> v<ver>(REQ-…)…"
git tag -f v<ver> && git push origin HEAD v<ver>
gh release create v<ver> dist/v<ver>/dsh-worktable.tgz dist/v<ver>/dsh-worktable-<ver>.tgz \
--draft --title "<name> v<ver>" --notes-file <notes.md>
```
- tag **必须等于包内版本**(`v` + `package.json#version`),否则下游按 tag 拼版本化资产名会 404。
- Release 附**双资产**:固定名(供 `releases/latest/download/<fixed>.tgz` 长期入口)+ 版本化名(可追责)。
## 阶段 2:发布线 B — 无上游写权限 → fork 发布
```bash
gh repo fork <upstream-owner>/<repo> --clone=false # 已存在则复用
git remote add fork git@github.com:<me>/<repo>.git # 已存在则 git remote set-url fork …
git fetch --no-tags origin main
```
### 2.1 干净单提交 + 树一致性 gate(必做,不可省)
```bash
OURS=$(git rev-parse <本地完成发布内容的 HEAD>)
git checkout -B release/v<ver> origin/main
git rm -rq --cached .
git checkout "$OURS" -- .
git add -A
git commit -m "release: <name> v<ver>(REQ-…)…"
git diff --stat "$OURS" # 必须是空输出
```
- `git diff --stat "$OURS"` **非空即 abort**:切回原分支、删掉该发布分支、报告差异,不要「差一点也推」。
- 该 gate 的真实作用是:让公开发布分支的**树**等于本地验收树,同时**不携带本地历史提交**(个人路径、调试、凭据)。
- `git rm -rq --cached .` 只清索引,随后 `git checkout "$OURS" -- .` 覆盖工作树;若上游 `main` 存在本地树里**已删除**的
文件,残留文件会被 `git add -A` 重新加回导致 gate 失败——先确认 `git diff --name-status origin/main <OURS>` 无 `D`/`R`。
- 提交前可顺手确认没有敏感路径混入:`git log -S'<本机绝对路径前缀>' --oneline`。
### 2.2 推分支 + tag + 附双资产的 draft Release
```bash
git push -u fork release/v<ver>
git tag -f v<ver> && git push -f fork v<ver> # -f 只作用于 fork 的 tag
gh release create v<ver> dist/v<ver>/dsh-worktable.tgz dist/v<ver>/dsh-worktable-<ver>.tgz \
--repo <me>/<repo> --draft --title "<name> v<ver>" --notes-file <notes.md>
```
- 推完复核「原仓库零改动」:`git ls-remote <origin-url>`(应无新分支/tag)+ `gh release list --repo <upstream>`(列表应与之前一致)。
- Release notes 至少写:新增 / 修复 / 清理 / 验证(实际跑过的测试与数字)/ 安装命令(指向 fork)/ 包 SHA-256 / 已知边界。
## 阶段 3:改指向(换仓库、换发布方时)
只要「发布方」变了,插件里所有指向旧仓库的常量都要一次改全,否则**更新检查读的还是旧仓库 → 永远发现不了新版**:
- 更新检查仓库常量(如 `UPDATE_REPO`,可能有多份:入口 + 独立模块 + 设置面板各自实现);
- 升级命令/提示词里的安装 URL(可能拼在字符串里);
- `package.json#repository.url`;
- 发布后核对脚本里硬编码的 REPO;
- README/AGENTS/安装说明里的一行命令、`git clone` 地址、文档链接;
- 任何 `grep -rn "<旧 owner>"` 的残留。
规则:
- **只改指向,不改版本号与逻辑**;逐处 `git diff` 复核。
- 「相关项目/其它仓库」的外链如果指向**另一位开发者的独立仓库**,不要顺手改指(目标仓库不存在会变成死链)——保留并在交付里说明。
- **改完必须重新 pack**(铁律 3),解包 `grep` 确认产物内无旧仓库名,sha 与 Release 全部按新值重来。
## 阶段 4:draft → publish
```bash
gh release edit v<ver> --repo <me>/<repo> --draft=false
```
- **draft 期间 `releases/latest` 仍指向上一个已发布版本** → 插件内「检查更新」看不到新版本;
draft 资产的 tag 路径也不公开可下(需鉴权)。这是「更新检查不生效」最常见的**非 bug 原因**,先查 Release 是否还是 draft。
- 发布前用用户过目确认;发布后再跑阶段 5。
## 阶段 5:发布后核对
优先用仓库自带的核对脚本,但**平台适配要先判定**:`verify-remote.mjs` 之类脚本若用
`powershell Invoke-WebRequest` 下载,则**只在 Windows 可用**,在 macOS 会以 `FAIL: download fixed-name asset` 收场
(它的 GET URL 仍能证明改指生效,不代表脚本思路错)。
macOS/无 PowerShell 时的等价核对(全部要过):
```bash
gh release view v<ver> --repo <me>/<repo> --json assets # 两个资产的 digest 必须都等于本地 sha
gh release download v<ver> --repo <me>/<repo> --dir "$TMP" # draft 也可下载(已鉴权)
shasum -a 256 "$TMP"/*.tgz # 与本地验收 sha 逐字一致
cmp "$TMP/dsh-worktable.tgz" dist/v<ver>/dsh-worktable.tgz # byte-identical
tar -xzf "$TMP/dsh-worktable.tgz" -C "$TMP/ext" && grep -rn "<旧仓库名>" "$TMP/ext" # 无输出
curl -sL -o /dev/null -w '%{http_code}\n' <版本化资产 URL> # 发布后 200/302;draft 阶段 404 属预期
```
结论口径:**文件名 + 结构(精确文件清单)+ 包内版本 + 双资产同源 + 远端=本地验收 SHA + 真实安装导入** 全过才算发布验收。
draft 阶段只能做「远端资产 = 本地验收包」的比对,匿名可下载性要等 publish 后再验。
## 阶段 6:Slark 宿主补丁(宿主侧特有)
- **改 `app.asar` 无效**:Electron 用 `ElectronAsarIntegrity` 记录 asar 头哈希并有 fuse 完整性校验,直接热改会让
哈希失配,破坏签名,应用拒绝启动或行为异常。**必须重打包 + 重新安装**,不要在安装目录里就地改文件。
- 宿主补丁落在 fork/分支上时:rebase 到发布基线后**必须重跑全量测试**(类型检查 + 全量单测),
例如 `pnpm -r typecheck` 与 `node --import tsx --test "src/**/*.test.ts"`;rebase 不产生冲突**不代表语义没变**。
- 宿主发布与插件发布解耦:宿主要用新插件版本,通常靠宿主内嵌 runtime 的升级,而不是「插件先发、宿主自动跟进」。
## 网络与链路故障处置
| 现象 | 判定 | 处置 |
| --- | --- | --- |
| `curl` 访问 GitHub 返回 HTTP `000`(HTTPS 整体不通) | 出口到 github.com:443 被掐断,与凭据无关 | 改走 SSH:`git remote set-url origin git@github.com:<owner>/<repo>.git`,并确认 SSH 身份 |
| `git fetch` 长时间无输出/0 字节、卡十几分钟 | 链路假死,不是"慢" | 判死并清理后重试(见下方循环模板),不要原地等 |
| 30 秒内报 `fatal: early EOF` | 链路被中途掐断(代理/MTU/网络策略) | 记录原文与时间点;换协议/换网络重试;不要反复 force |
| macOS 没有 `timeout` 命令 | BSD 无 GNU timeout | 用后台 + 轮询模板 |
| `git push` 报无权限但 ls-remote 正常 | 只读凭据 | 直接走 fork 发布(阶段 2),不要重试上游 |
macOS 无 `timeout` 时的判死模板:
```bash
( git fetch --no-tags origin main ) & PID=$!
for i in $(seq 1 30); do sleep 1; kill -0 "$PID" 2>/dev/null || break; done
if kill -0 "$PID" 2>/dev/null; then kill -9 "$PID"; pkill -9 -f git-upload-pack; echo "TIMEOUT(30s) — 链路假死"; fi
```
- 认证/网络失败时**不做破坏性重试**(尤其不要为了"绕过"而 force 到上游);把失败原文、已完成的步骤、当前指针状态写清楚上报。
- 推送成功但平台上看不到:查 `git config --get-regexp '^url\.'` 是否有 `pushInsteadOf` 静默改写了目标。
## ai-proj 对齐
- 需求阶段:动手前立项(`create_requirement`,写清需求描述 / 预期结果 / 验收标准三段式 + AC),commit/PR 引用 `REQ-…`。
纯文档/工具/只读排查可例外。
- 交付阶段:**真实发布之后**才推进 `staging → released`;只有计划、没有发布产物或验收证据时不要推进。
- 归档阶段:`/req done` 归档前要**逐条 AC 证据**(命令、输出、资产 sha、URL),不能只写"已完成"。
- **文档接口故障绕过**:若 `append-document-content` / `create-and-attach` 持续报
`document_versions_document_id_version_number_key` 唯一约束错误(同一 `(document_id, version_number)` 冲突),
改用 `PUT /api/v1/requirements/{id}` 把说明写进**需求描述**——`requirements` 表不经过 `document_versions`,可绕开该约束;
不要伪造版本号或删除已有文档版本。
## 故障分流
| 现象 | 处置 |
| --- | --- |
| 插件「检查更新」永远看不到新版 | 依次查:Release 是否还是 draft;`releases/latest` 指向;插件内 `UPDATE_REPO` 是否仍指旧仓库;`latest` 资产名是否与代码拼的一致 |
| pack 报结构断言失败 | 包内混入了 `src/`、`.map` 或多余文件;检查 `files` 字段与构建产物位置,不要手改 tgz |
| 两个 tgz 的 sha 不一致 | 打包脚本的"同源复制"被绕过(手工 pack/改名);重新走唯一入口 |
| `git diff --stat <OURS>` 非空 | 上游 `main` 有本地已删除/重命名文件残留 → 先 `git rm --cached` 清理并确认无 `D`/`R`,gate 不过不上推 |
| 发布分支含个人路径/凭据 commit | 不要 push 该历史;改用干净单提交分支(阶段 2.1) |
| `gh release create` 报 tag 不存在/被占用 | tag 必须已存在或由 `create` 指定;draft 会以 `untagged-…` 预览 URL 呈现,属正常,publish 后即 `.../tags/v<ver>` |
| draft 资产匿名下载 404 | 预期行为(draft 需鉴权);发布后用 `curl -sL -o /dev/null -w '%{http_code}'` 复验 |
| 宿主改完不生效 | 是否只改了 `app.asar`(被完整性校验挡住);必须重打包重装;再查是否加载了另一份 runtime |
## 参考
- `references/fork-release-and-post-checks.md` —— fork 发布可复制命令序列、Release notes 模板、发布后核对矩阵。
- 同市场:`slark-cicd`(Slark staging/生产/Desktop)、`dev-cicd`(流水线设计)、`ai-proj-cicd-release`(AI-Proj 发布链)。
- 权威流程以目标仓库自带的发布脚本与文档为准;本技能是清单,不替代仓库脚本的断言。
@@ -0,0 +1,134 @@
# Fork 发布与发布后核对
本文件是 `slark-dsh-cicd` 的操作附件:需要**无上游写权限发布**,或需要**逐项核对远端资产**时读取。
把它当模板:替换 `<...>` 占位符,按阶段执行,每阶段的核验不通过就停下,不要"带病"推进。
## 0. 变量与前置
```bash
export HOME="/Users/johnq"
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:/opt/homebrew/bin:$PATH"
UP=<upstream-owner>/<repo> # 例如 Aisland-SJL/dsh-worktable(外部作者,无写权限)
ME=<your-account> # 例如 qiudl
PKG=<repo>/01_content # 打包脚本所在目录
VER=0.4.1 # = package.json#version
```
```bash
gh auth status
ssh -T git@github.com 2>&1 | head -3
gh api "repos/$UP" --jq '.permissions' # push:false → 必须走 fork 发布
git remote -v
```
## 1. 改指向 → 重新打包(顺序不可颠倒)
```bash
cd "$PKG"
grep -rn "<upstream-owner>" .. --include='*.json' --include='*.ts' --include='*.tsx' --include='*.md' --include='*.mjs'
# 逐处改完之后再打包:
npm run check && npm run test:gate && npm run pack
shasum -a 256 dist/v$VER/*.tgz
TMP=$(mktemp -d) && tar -xzf dist/v$VER/dsh-worktable.tgz -C "$TMP"
grep -rn "<upstream-owner>" "$TMP" ; echo "grep exit=$? (1 = 无残留,正确)"
```
判据:`check`/`test:gate`/`pack` 全部 exit 0;两个 tgz 同 sha;解包无旧仓库名。
## 2. fork 与干净发布分支
```bash
cd <repo-root>
gh repo fork "$UP" --clone=false # 已存在会提示 already exists,可复用
git remote add fork "git@github.com:$ME/<repo>.git" # 已存在:git remote set-url fork …
git fetch --no-tags origin main
OURS=$(git rev-parse HEAD) # 本地"发布内容已就绪"的 HEAD
git diff --name-status origin/main "$OURS" | awk '$1 ~ /^[DR]/' # 期望空;有 D/R 先处理再用 gate
git checkout -B release/v$VER origin/main
git rm -rq --cached .
git checkout "$OURS" -- .
git add -A
git commit -m "release: <name> v$VER(REQ-…)<一句话说明>"
DIFFOUT="$(git diff --stat "$OURS")"
if [ -n "$DIFFOUT" ]; then
echo "GATE FAILED:"; echo "$DIFFOUT"
git checkout <原分支> && git branch -D release/v$VER # abort:不留半成品分支
exit 1
fi
echo "GATE PASSED: release tree == OURS tree"
```
## 3. 推送(只对 fork)与 draft Release
```bash
git push -u fork release/v$VER
git tag -f v$VER && git push -f fork v$VER # -f 仅限自己 fork 的 tag
gh release create v$VER \
dist/v$VER/dsh-worktable.tgz dist/v$VER/dsh-worktable-$VER.tgz \
--repo "$ME/<repo>" --draft --title "<name> v$VER" --notes-file /tmp/rel-notes.md
```
原仓库零改动复核:
```bash
git ls-remote "git@github.com:$UP.git" # 无 release/v… 分支、无新 tag
gh release list --repo "$UP" # 与发布前一致
```
### Release notes 模板(draft 阶段就写全新值)
```markdown
# <name> v<ver>
> 发布方:fork <ME>/<repo>(原仓库 <UP> 为上游来源)。插件内更新检查已指向本 fork。
## 新增 / 修复 / 清理
## 验证
- check / gate / pack 实际输出(测试条数、断言项)
- 双资产同 SHA:<sha256>
- 解包 grep 无旧仓库名
- 发布分支为干净单提交(父提交 = 上游 main),`git diff --stat <OURS>` 为空
## 安装
网页端 / 桌面端一行命令(指向 fork)
## 已知边界
- draft 期间 releases/latest 仍指旧版 → 更新检查看不到新版
- 保留的第三方外链、平台未验证范围
```
## 4. publish 与发布后核对矩阵
```bash
gh release edit v$VER --repo "$ME/<repo>" --draft=false
```
| 核对项 | 命令 | 通过判据 |
| --- | --- | --- |
| 资产列表与 digest | `gh release view v$VER --repo $ME/<repo> --json assets` | 两个资产 `digest` = 本地 sha |
| 下载后 sha | `gh release download v$VER --repo $ME/<repo> --dir "$D"` + `shasum -a 256 "$D"/*.tgz` | 与本地逐字一致 |
| 与本地验收件一致 | `cmp "$D/dsh-worktable.tgz" dist/v$VER/dsh-worktable.tgz` | 无差异(byte-identical) |
| 包内无旧仓库名 | 解包后 `grep -rn "<upstream-owner>"` | 无输出(exit 1) |
| 包内版本 = tag | 解包读 `package.json#version` | `v$VER` |
| 匿名可下载 | `curl -sL -o /dev/null -w '%{http_code}\n' <资产 URL>` | publish 后 200/302;draft 期 404 属预期 |
| 安装可用 | 临时目录 `npm install <tgz>` + `import()` 断言 | 导出/版本断言通过 |
- **`verify-remote.mjs` 的平台限制**:其下载走 `powershell Invoke-WebRequest`,只在 Windows 可用;
在 macOS 会 `FAIL: download fixed-name asset`。此时用上表的 `gh release view/download` + `shasum` + `cmp` 等价替代,
并说明该脚本的 GET URL 已证明改指生效。
- **"检查更新"看不到新版**的排查顺序:Release 是否 draft → `releases/latest` 指向 → 插件内 `UPDATE_REPO` 指向 →
`latest` 资产名与代码拼法一致。
## 5. 链路判死模板(macOS 无 `timeout`)
```bash
( git fetch --no-tags origin main ) & PID=$!
for i in $(seq 1 30); do sleep 1; kill -0 "$PID" 2>/dev/null || break; done
if kill -0 "$PID" 2>/dev/null; then kill -9 "$PID"; pkill -9 -f git-upload-pack; echo "TIMEOUT(30s)"; fi
```
- HTTPS 到 GitHub `curl` 返回 `000` → 整体不通,改 SSH(`git remote set-url`)。
- 30 秒内 `fatal: early EOF` → 链路被掐断;记录原文与时间,换网络/协议重试。
- 认证或网络失败时不做破坏性重试,也不要把 force 用到上游;保留本地提交并上报失败原文。
+54
View File
@@ -322,6 +322,59 @@ npm run lint
---
## ⚠️ 既有契约与读模型迁移门禁(必须)
> **惨痛教训**:新写链路把真实数据写入新表,旧 API 仍读原表,前端虽然没改却从此看不到新数据。“后端已成功执行”不等于“用户能看到执行结果”。
开发计划涉及新表、新账本、新写路径、替换存储模型或接口版本时,必须先完成以下门禁。未通过时,不得把“新建平行表/新建平行 API”写成默认方案。
### 1. 先证明旧模型不能复用
- [ ] 已检查现有表、字段、索引、状态机和扩展字段(如 metadata/JSON)。
- [ ] 已说明为什么增加字段、子表或现有类型无法满足需求。
- [ ] 新表承载的是真正独立的实体或生命周期,而不是为同一业务实体建立第二个彼此脱节的世界。
- [ ] 已区分“内部不可变证据/事件账本”和“产品对外读模型”;新建内部账本不代表必须更换原有产品接口。
### 2. 真实调用方查证
必须从对外接口向所有消费方反向追踪,不能只审查新后端写路径:
- [ ] 列出现有 API 路径、请求参数、响应结构和身份/分页语义。
- [ ] 使用 `rg` 查找 Web、iOS、Android、定时任务、报表和第三方调用方。
- [ ] 若仓库存在 `.codex-fe-ref/`,必须在其中查证配套前端的真实调用。
- [ ] 开发计划的变更文件清单必须覆盖“写入 → 投影/查询 → API → 页面”完整链路。
### 3. 稳定契约默认不变
- 既有用户界面已消费的 API,默认保留路径和响应契约;除非用户明确批准破坏性迁移,不得通过新建版本接口把兼容成本转嫁给前端。
- 同一业务概念只保留一个产品级读模型。内部账本可以拆分,但必须投影回既有读模型,或由原 API 无感聚合。
- 日志、告警、信号或推测数据不得在查询降级时冒充业务事实(例如把 warning 当成交易记录)。
### 4. 不可避免新写模型时的强制迁移计划
若确有独立生命周期、不可变审计或 1:N/N:N 证据需求,可以增加新表,但开发计划必须同时包含:
1. **写入兼容**:双写,或从新账本向既有读模型做事务性/幂等投影。
2. **历史回填**:有界、可恢复、可重跑,且不猜测无法证明的归属。
3. **去重身份**:定义跨旧/新数据源的稳定业务键,并用数据库唯一约束或等价强保证防重。
4. **读取切换**:定义何时以新证据为准、旧数据如何兼容,以及结果数量/金额/状态对账门槛。
5. **回滚策略**:回滚新写入时仍能读取已产生的真实业务事实,不删除或隐藏已成功数据。
6. **可观测性**:监控新写入成功但旧读模型缺失、投影延迟、去重冲突和回填差异。
### 5. 用户可见性是必须验收项
测试不得只证明“新写路径成功”。至少要有一个真实业务记录贯穿测试,同时证明:
- [ ] 新写路径产生了正确事实。
- [ ] 原有对外 API 可以立即或在约定 SLA 内返回该记录。
- [ ] 每个真实消费页面/客户端都能正确解码并展示。
- [ ] 时间、方向、状态、数量、金额与唯一标识在数据库、API 和 UI 之间一致。
- [ ] 部分失败、重试、重复投递、迟到数据和回滚后仍不丢数、不重复、不冒充。
开发计划必须把上述内容落成明确的变更文件、任务、测试用例和发布门禁,不得只在“风险”章节留一句提醒。
---
## 开发计划文档模板
```markdown
@@ -1695,3 +1748,4 @@ migrate -path migrations -database "postgres://..." up
|------|------|----------|--------|
| V1.0 | 2026-01-26 | 初始版本,创建 req-dev 技能 | Claude + qiudl |
| V1.1 | 2026-01-29 | 添加 iOS 端开发规范(SwiftUI + MVVM 架构) | Claude Opus 4.5 |
| V1.2 | 2026-08-26 | 新增既有契约与读模型迁移强制门禁,防止新写链路与原 API/前端脱节 | Codex |
@@ -1,7 +1,7 @@
{
"name": "req-plugin",
"description": "需求工作流管理入口。用于 /req 命令使用、需求生命周期管理、需求撰写(含草稿门禁)。",
"version": "2.0.0",
"version": "2.1.0",
"author": {
"name": "qiudl"
},
+5 -2
View File
@@ -48,6 +48,7 @@ analysis → design → dev → review → testing → [待部署池] → releas
- **操作前先确认实际 ID** — 从 URL 提取 ID(如 `/requirements/897` → ID=897)
- **务实路线关闭也必须补全关联任务** — 每个进度条阶段需创建关联任务
- **阶段内容门禁(防空转)** — `/req next` 时检查关键阶段任务是否有实质内容
- **复用盘点先于估工(Gate 5,强制)** — PRD 必须含「现有实现与复用盘点」章节:每个能力点给出**现有实现(file:line)+ 结论(复用 / 改造 / 新增)**;结论=复用/改造的只按「接入差异」估工,结论=新增的必须写「为什么现有实现不能满足」。没有 file:line 证据、或标「未排查」的,**禁止 submit、禁止按全新增估工**;独立复查由非 PRD 写作者用 grep 复核并在 note 留命令与命中结果
- **Harness 环境检测** — `/req dev` 启动开发前,快速检测项目基础设施(`.husky/` 存在?`scripts/check-*.sh` 存在?)。若两者都不存在(Level 0),输出一行提示:「项目无质量护栏,建议先运行 `/harness init`」。检测结果不阻塞开发,仅提示
## 禁止直接调用(必须走命令流程)
@@ -55,7 +56,7 @@ analysis → design → dev → review → testing → [待部署池] → releas
| 禁止直接调用 | 必须通过 | 原因 |
|-------------|---------|------|
| `ai-proj req create` with allowSelfApprove | `/req new` | 禁止自动提交,必须创建为 draft |
| `ai-proj req submit` (直接调用) | `/req submit` | 必须先通过 4 道门禁检查 |
| `ai-proj req submit` (直接调用) | `/req submit` | 必须先通过 5 道门禁检查 |
| `ai-proj req archive` | `/req done` | 必须先执行类型化门禁检查 |
| `ai-proj req approve` | `/req review pass` | 必须先展示 PRD 摘要并等待用户确认 |
| `ai-proj req advance --to <stage>` (force) | `/req next` | 必须先 AskUserQuestion 确认 + 记录跳过原因 |
@@ -73,13 +74,15 @@ ai-proj req create \
# → 结果必定是 draft 状态
```
### 从 draft 到 submit 的 4 道门禁
### 从 draft 到 submit 的 5 道门禁
```
Gate 1: 需求讨论 ── description 末尾有「## 讨论结论」章节
Gate 2: 方案确认 ── AskUserQuestion 获得「方案确认通过」
Gate 3: PRD 文档 ── linkRole=prd 的任务存在且有文档
Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD 存在
Gate 5: 复用盘点 ── PRD 有「现有实现与复用盘点」章节;每条能力点有 file:line 证据
或「为什么不能复用」的理由;估工按 复用/改造/新增 分别计(无盘点 = 阻断)
```
## 完整工作流
@@ -1,7 +1,7 @@
{
"name": "req-prd-plugin",
"description": "产品需求设计技能。覆盖问答、PRD、缺陷与 OSS 原型闭环,文档双写本地和 ai-proj。",
"version": "2.2.0",
"version": "2.3.0",
"author": {
"name": "qiudl"
},
+15
View File
@@ -137,6 +137,21 @@ description: 产品设计与需求管理。用于 PRD、需求分析、用户故
- 会议/邮件/聊天记录链接
- 客户特殊约束(如"必须在 Q2 前上线")
### 1.5 现有实现与复用盘点 ⭐ 强制
> **目的**:防止把「已经有的能力」当成新增来估工和开发(真实事故:DS Bot 早已有头像上传/裁剪功能,PRD 仍按新建估工)。
> **检索证据**:每条都要给 `file:line`(或 grep 命令 + 命中结果),不接受「凭印象认为没有」。
| 能力点 | 现有实现(file:line) | 结论 | 说明 |
|--------|----------------------|------|------|
| 头像上传/裁剪 | `ds-bot/src/client/bot-settings.js:xx` | 复用 | 只改存储落点与主体 |
| …(本次涉及的每个能力点都要有一行) | | | |
**硬规则**:
- 结论是 **复用 / 改造** 的,估工只算「接入差异」,不许按全新功能算。
- 结论是 **新增** 的,必须写「为什么现有实现不能满足」。
- 标「未排查」或没有 `file:line` 的 PRD **不得提交评审**;独立复查人(非 PRD 作者)要用 grep 复核并在 note 留证据。
## 2. 用户分析
### 2.1 目标用户
[用户画像描述]
+3
View File
@@ -42,6 +42,9 @@ description: Installer fixture version two.
---
version two
EOF
# Reproduce rsync's quick-check edge case: changed content with identical size
# and mtime must still replace the recorded, unmodified installation.
touch -r "$TEST_HOME/.agents/skills/example/SKILL.md" "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md"
HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null
grep -q 'version two' "$TEST_HOME/.agents/skills/example/SKILL.md"
grep -q '"version": "2.0.0"' "$TEST_HOME/.agents/.ai-proj-helper-installed-skills.json"
+1
View File
@@ -49,6 +49,7 @@ PLUGIN_MAP = {
"karpathy-guidelines-plugin": ("karpathy-guidelines", "skill", "dev"),
"pull-request-plugin": ("pull-request", "skill", "dev"),
"review-checklist-plugin": ("review-checklist", "skill", "dev"),
"slark-dsh-cicd-plugin": ("slark-dsh-cicd", "skill", "dev"),
# skills-integration
"data-excel-plugin": ("data-excel", "skill", "integration"),