Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9ec9f15
build(ci): 补齐宿主清单版本 bump 与范围修订记录(check:version 判红闭环)
stack-wuh Oct 10, 2026
6775f1f
docs(shadow): brief 最终态——committed
stack-wuh Oct 10, 2026
7b53a8f
docs(shadow): review 结论重钉至最终提交(补齐版本 bump 后全门复绿)
stack-wuh Oct 10, 2026
3c23fbf
docs(shadow): brief 最终态——committed
stack-wuh Oct 10, 2026
3336349
docs(knowledge): 落地本仓上下文地图,卡片 domain 升为一等路由键并进门校验
stack-wuh Oct 10, 2026
c566cd1
fix(ci): windows 同步内自带 ~/.local/bin——$GITHUB_PATH 对后续步骤才生效致 exit 127
Oct 10, 2026
0b622b6
docs(shadow): brief 最终态——committed
Oct 10, 2026
47f5b54
docs(shadow): 登记 Phase 4 跨上下文声明(distribution 两处,经用户裁决接受)
Oct 10, 2026
0c6c3a4
docs(shadow): brief 最终态——committed
Oct 10, 2026
715eed5
docs(shadow): brief 最终态——published, PR #45
Oct 10, 2026
b08f642
fix(ci): strict 门在 win32 经 shell 解析 .cmd shim,命令目录不再恒 unresolved
Oct 10, 2026
edd342c
docs(shadow): brief 最终态——committed
Oct 10, 2026
670792d
docs(shadow): brief 最终态——published, PR #44
Oct 10, 2026
796faac
fix(ci): 知识治理门读入归一行尾,windows autocrlf 检出不再误报技能缺 frontmatter
Oct 10, 2026
dbd75fa
docs(shadow): brief 最终态——committed
Oct 10, 2026
919d2ad
docs(shadow): brief 最终态——published, PR #44
Oct 10, 2026
9ceb70f
fix(test): pack 解包按 posix 路径交给 bash 并响亮报告失败,退役 SGN-010
Oct 10, 2026
1df7ba1
docs(shadow): brief 最终态——committed
Oct 10, 2026
009c808
docs(shadow): brief 最终态——published, PR #44
Oct 10, 2026
1e3f9a6
fix(test): pack 失败路径同享清理钩子,不再给仓库留未跟踪 dist/
Oct 10, 2026
202d792
docs(shadow): brief 最终态——committed
Oct 10, 2026
c859990
docs(shadow): brief 最终态——published, PR #44
Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "shadow-dev-workflow",
"description": "Self-contained development workflow with 3-tier knowledge base — 5-phase pipeline (propose → apply → review → release → archive) with packaged planning, domain-model-driven specs, invariant-based verification, and knowledge-driven design constraints. Zero external workflow dependencies.",
"version": "6.6.0",
"version": "6.8.0",
"author": {
"name": "Shadow (stack-wuh)"
},
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shadow-dev-workflow",
"version": "6.6.0",
"version": "6.8.0",
"description": "Self-contained development workflow with 3-tier knowledge base — propose → apply → review → release → archive with packaged planning, domain-model-driven specs, invariant-based verification, and knowledge-driven design constraints. Zero external workflow dependencies.",
"author": {
"name": "Shadow (stack-wuh)",
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ jobs:
PIN=$(node -pe 'require("./package.json").cliVersion')
bash scripts/install-cli.sh install --version "$PIN"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
# 同 quality-gate.yml:$GITHUB_PATH 对后续步骤生效,本步内紧接着的冒烟需自带 PATH(当前 job 跑 ubuntu,防御性对齐,避免两条流水语义漂移)。
export PATH="$HOME/.local/bin:$PATH"
shadow-dev --version

- name: 发布前全门(version + requires:strict + knowledge + test)
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/quality-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ jobs:
PIN=$(node -pe 'require("./package.json").cliVersion')
bash scripts/install-cli.sh install --version "$PIN"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
# $GITHUB_PATH 只对后续步骤生效;同一步内紧接着的冒烟必须自带 PATH,
# 否则 windows-latest(Git Bash 默认不含 ~/.local/bin)判红而 ubuntu/macos 靠镜像默认值蒙过。
export PATH="$HOME/.local/bin:$PATH"
shadow-dev --version

- name: 版本同源门
Expand Down
3 changes: 2 additions & 1 deletion knowledge/domain-driven-shadow-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ scope: [norms/domain-model.md, norms/verification.md, skills/shadow-dev-propose,
status: active
source:
- changes/20261010-refactor-domain-driven-workflow/brief.md
- changes/20261010-feature-workflow-context-map/brief.md
verified: 2026-10-10
verified-depth: unit
verified-scope: 本变更以自身结构自证——L 级 brief 含「## 领域模型」段且不变量清单非空,`change create`/`change approve`/`task list`(12 项)与 `worktree inspect` 的 `评级: L` 正则提取全部真实跑通;既有 `node --test test/pack.test.mjs` 4/4 绿。runtime 观察点(一致性门、产物扫描、拒绝路径)待 release 证据补齐后再升深度。
verified-scope: 本仓已落地 `shadow-docs/domain.md` 上下文地图,卡片 `domain` 值域改由 `scripts/check-knowledge.mjs` 机器判定(本卡验证方式第 4 条由口头要求变为可执行断言,拒绝路径已实跑判红);原自证记录:本变更以自身结构自证——L 级 brief 含「## 领域模型」段且不变量清单非空,`change create`/`change approve`/`task list`(12 项)与 `worktree inspect` 的 `评级: L` 正则提取全部真实跑通;既有 `node --test test/pack.test.mjs` 4/4 绿。runtime 观察点(一致性门、产物扫描、拒绝路径)待 release 证据补齐后再升深度。
---

# 领域模型是工作流的规格载体
Expand Down
5 changes: 3 additions & 2 deletions knowledge/multi-host-plugin-distribution.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
---
title: 插件多宿主分发(Claude Code / Codex / zcode)
domain: 插件分发
domain: distribution
keywords: [Codex, Claude Code, 插件清单, marketplace, hooks, 宿主适配, 打包分发, 技能加载]
scope: [.codex-plugin/, .claude-plugin/, .agents/, hooks/, adapters/, scripts/pack.mjs, skills/]
status: active
source:
- changes/20261009-feature-codex-host-support/brief.md
verified: 2026-10-09
- changes/20261010-feature-workflow-context-map/brief.md
verified: 2026-10-10
verified-depth: runtime
verified-scope: 本机 Codex CLI 0.160.1 实测——`codex plugin marketplace add D:/works/shadow-dev-workflow` 回 `Added marketplace shadow-dev-workflow-local`;`codex plugin add shadow-dev-workflow@shadow-dev-workflow-local` 落地 `~/.codex/plugins/cache/shadow-dev-workflow-local/shadow-dev-workflow/6.4.0`(8 技能 + menu/norms/knowledge/rules 齐备,逐文件哈希与源一致);重启后技能表出现 `shadow-dev-workflow:shadow-dev-*` 命名空间条目;探针版 `commandWindows` 在 `codex exec --dangerously-bypass-hook-trust` 下输出 `hook: SessionStart` → `SessionStart Completed` 并落标记文件
---
Expand Down
6 changes: 4 additions & 2 deletions knowledge/release-artifact-pipeline.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
---
title: 发布链版本权威与产物资产契约
domain: distribution
keywords: [发布, release, tag, 版本权威, 资产名, tarball, pack, 上传, 发布冒烟, quality-gate, publish-release, CI, strict, 半绿]
keywords: [发布, release, tag, 版本权威, 资产名, tarball, pack, 上传, 发布冒烟, quality-gate, publish-release, CI, strict, 半绿, GITHUB_PATH, PATH, shim]
scope: [scripts/pack.mjs, scripts/bump-version.mjs, scripts/check-requires.mjs, scripts/check-knowledge.mjs, .github/workflows/]
status: active
source:
- changes/20261010-build-quality-gate/brief.md
- changes/20261010-feature-workflow-context-map/brief.md
verified: 2026-10-10
verified-depth: unit
verified-scope: 本机 `npm run ci` 全绿(版本同源 + requires:strict + 知识治理门 + 既有 11 例测试);strict 反例用 `SHADOW_DEV_CLI` 指向坏文件复现 rc=1,非 strict 同条件复现半绿(rc=0 且输出「CLI 目录未参与断言」);版本反例单改一份 manifest 复现 rc=1 后回滚;三个 workflow 经 YAML 解析。GitHub 矩阵真跑与发布冒烟待首个 PR / 首个 release 的 run 链接。
verified-scope: 本机 `npm run ci` 全绿(版本同源 + requires:strict + 知识治理门 + 既有 11 例测试);strict 反例用 `SHADOW_DEV_CLI` 指向坏文件复现 rc=1,非 strict 同条件复现半绿(rc=0 且输出「CLI 目录未参与断言」);版本反例单改一份 manifest 复现 rc=1 后回滚;三个 workflow 经 YAML 解析。GitHub 矩阵真跑与发布冒烟待首个 PR / 首个 release 的 run 链接。新增的 `$GITHUB_PATH` 同步内不可见一条:失败侧已由 PR #44 的 `quality-gate` windows 两项 `exit 127` 实证(同 run ubuntu/macos 绿),修复生效待本 PR 转绿后的 run 链接回填。
---

# 发布链版本权威与产物资产契约
Expand All @@ -27,6 +28,7 @@ GitHub Release 是产物与版本的**唯一权威发布点**,四件事缺一
- 发版顺序恒为:`node scripts/bump-version.mjs <ver>` → `npm run check:version` → 提交合并 → `git tag v<ver>` → `gh release create`;此后判定、pack、资产断言、上传、冒烟由 `publish-release.yml` 接手。
- 新增机器门一律以 strict 形态进 CI;宽松模式只允许作为本机开发路径,且必须自曝降级态(如 `catalog=unresolved`)。
- `quality-gate.yml` 覆盖 push main 与所有 PR,矩阵含 windows——`tar`/`bash` 的跨平台坑已被实证,只跑 ubuntu 等于把最难发现的回归留在暗处。AI 的本机口头自证不可替代机器门。
- 同一 `run:` 块里**不得依赖 `$GITHUB_PATH` 取刚装的 shim**:`echo ... >> "$GITHUB_PATH"` 只对**后续步骤**生效,紧跟着的冒烟命令仍用旧 PATH。`ubuntu`/`macos` runner 镜像默认 PATH 含 `~/.local/bin` 会蒙过,`windows` 的 Git Bash 不含即 `command not found` 退 127——同一步必须自己 `export PATH="$HOME/.local/bin:$PATH"`(实证:PR #44 的 `quality-gate` windows 两项挂在此处,ubuntu/macos 同 run 全绿)。
- 归档会搬迁 brief:卡片 `source` 按规范写 `changes/<name>/brief.md`,治理门必须向 `changes/archive/<name>/` 兜底解析(否则每张卡在来源 change 归档那天集体判红——本仓实测 3 张跨项目卡中 2 张如此)。

## 适用边界
Expand Down
4 changes: 3 additions & 1 deletion norms/domain-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
| 评级 | 要求 |
|------|------|
| **L** | 必填:限界上下文 + 统一语言增量 + **非空**聚合与不变量清单 + 领域事件 |
| **M** | 必填一行归属上下文;触及既有不变量时列出受影响的不变量 |
| **M** | 必填「## 领域模型」段的**最小形态**:一行 `限界上下文`(含上下游)+ 被触及的既有不变量清单(确无则写「无」);统一语言增量与领域事件可省 |
| **S** | 免填 |

```markdown
Expand Down Expand Up @@ -69,6 +69,8 @@
- 不在实现中途新增上下文或改写归属;需要就回 propose 修订 brief。
- 不借「领域建模」扩大改动面:模型只描述本次变更触碰的不变量,不做全仓领域普查。
- 不把实现过程写进上下文地图——地图是长期事实,过程留在 brief。
- 不把横切实践(如 `debugging`)当限界上下文扩写;地图只登记归属与关系,概念定义仍由本文件唯一持有。
- 卡片 `domain` 取值必须来自地图登记名(**domain 值域**):禁止同义词、中英别名与临时自造——别名会把一个上下文切成两个查询域,路由与查重同时失效。该约束由 `scripts/check-knowledge.mjs` 判定。
- 一个 change 一个主上下文;跨上下文连带改动各自立项。

## 验证方式
Expand Down
10 changes: 8 additions & 2 deletions norms/knowledge-cards.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
- `norms/`:跨项目硬规则与工程规范。
- `knowledge/`:跨项目经验与协作知识。
- 项目 `shadow-docs/knowledge/`:项目独有的执行真相。
- 项目 `shadow-docs/domain.md`:上下文地图,卡片 `domain` 字段的**值域来源**(判据见 `norms/domain-model.md`)
- `brief.md`:记录单次变更发生了什么,不承担当前规范职责。

## 卡片格式

```markdown
---
title: <稳定领域事实名称>
domain: <主领域>
domain: <限界上下文,须能在项目 shadow-docs/domain.md 找到>
keywords: [<业务词>, <技术词>, <常见现象>]
scope: [<目录、包、路由或业务域>]
status: active
Expand Down Expand Up @@ -43,6 +44,10 @@ verified-scope: <本次验证观察到的具体现象与证据形态>
- [<相关卡片>](other-topic.md)
```

`domain` 是**路由键不是标签**:取值必须来自项目上下文地图 `shadow-docs/domain.md` 的登记名;新上下文先登记再建卡;禁止同义词、中英别名与临时自造值。

`source` 与归档兜底:卡片按知识库根写 `changes/<name>/brief.md`,而归档流程会把该 brief 搬进 `changes/archive/<name>/`——校验一律**向归档兜底解析**(本仓由 `scripts/check-knowledge.mjs` 执行),禁止为了让门变绿而改写卡片中的来源路径。

`status` 只允许 `active` 或 `deprecated`。`source` 使用相对于知识库根目录的 brief 路径;`verified` 只在通过代码或可重复检查确认结论仍有效时更新。

## 验证深度分级(verified-depth,20260925-feature-validation-strategy-signals 起)
Expand All @@ -69,7 +74,8 @@ verified-scope: <本次验证观察到的具体现象与证据形态>
- 不复制 brief 的实现过程与一次性验证输出。
- 新增前按 `domain + keywords + scope` 查重;能原位更新时不新增。
- active 卡片必须出现在 menu 的至少一条路由中。
- source 必须存在,scope 必须明确。
- source 必须存在(含向 `changes/archive/` 的兜底解析),scope 必须明确。
- `domain` 值必须能在项目上下文地图中找到;地图缺失时本项降级为提示,不阻塞尚未建图的仓。
- deprecated 卡片必须指向替代卡片或明确写明无替代。

## 生命周期
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shadow-dev-workflow",
"version": "6.7.0",
"version": "6.8.0",
"type": "module",
"engines": {
"node": ">=20"
Expand Down
31 changes: 30 additions & 1 deletion scripts/check-knowledge.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ const fails = [], warns = [], oks = []
const ok = m => oks.push(`[ok] ${m}`)
const warn = m => warns.push(`[warn] ${m}`)
const fail = m => fails.push(`[fail] ${m}`)
const read = p => readFileSync(join(root, p), 'utf8')
// 读容忍 CRLF、判读恒 LF:windows runner 的 actions/checkout 按 core.autocrlf 检出 CRLF,而本门的
// frontmatter 定界(`---\n`)、表格与引用扫描全部按 \n 解析——不归一会让 8 个技能集体误报「缺 name/description」
// (实证:PR #44 的 windows gate 第 7 步 fail=21,ubuntu/macos 同 run 全绿)。与 shadow-dev-cli 的 brief 行尾契约同源。
const read = p => readFileSync(join(root, p), 'utf8').replaceAll('\r\n', '\n')
const SKIP_DIRS = new Set(['.git', 'dist', 'node_modules', '.agents'])

// 1) 知识卡与规范:frontmatter 字段、status 枚举、source 存在性、active 卡路由覆盖
Expand Down Expand Up @@ -75,6 +78,32 @@ for (const dir of cardDirs) {
}
ok(`知识卡 ${cards.length} 张:字段/枚举/source/路由检查完成`)

// 1b) domain 值域:卡片 domain 必须是项目上下文地图(shadow-docs/domain.md)的登记名。
// 值域取「限界上下文 / 横切实践 / 外部上游」三张表首列的反引号名称;横切实践可作 domain 值,但不是业务上下文。
// 地图缺失只 warn:尚未建图的仓不该被本门的规范升级卡住(与「存量卡片不回溯迁移」同源)。
const DOMAIN_SECTIONS = ['## 限界上下文', '## 横切实践', '## 外部上游']
function loadDomainVocab() {
if (!existsSync(join(root, 'shadow-docs', 'domain.md'))) return null
const vocab = new Set()
let inSection = false
for (const line of read('shadow-docs/domain.md').split('\n')) {
if (line.startsWith('## ')) { inSection = DOMAIN_SECTIONS.some(k => line.trim().startsWith(k)); continue }
if (!inSection || !line.startsWith('|')) continue
const m = /^`([^`]+)`$/.exec((line.split('|')[1] || '').trim())
if (m) vocab.add(m[1])
}
return vocab
}
const domainVocab = loadDomainVocab()
let domainChecked = 0
for (const { rel, data } of cards) {
if (!data.domain) continue
if (!domainVocab) { warn('无法校验卡片 domain:缺 shadow-docs/domain.md 上下文地图(后续卡片须先建图)'); break }
if (domainVocab.has(data.domain)) { domainChecked++; ok(`${rel}: domain「${data.domain}」∈ 地图值域`) }
else fail(`${rel}: domain「${data.domain}」不在上下文地图值域内(合法值: ${[...domainVocab].join(', ')})`)
}
if (domainVocab) console.log(`[.] 上下文地图值域: ${[...domainVocab].join(', ')}(命中 ${domainChecked}/${cards.length} 卡)`)

// 2) 技能清单:SKILL.md 必有 name + description,且 name 与目录同名(宿主按 name 发现技能)
const skillDirs = readdirSync(join(root, 'skills')).filter(d => statSync(join(root, 'skills', d)).isDirectory())
for (const d of skillDirs) {
Expand Down
10 changes: 9 additions & 1 deletion scripts/check-requires.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,16 @@ const argv = cli ? [process.execPath, [cli, 'help', '--full', '--json']] : ['sha
// 整段 JSON.parse 必然抛错——本门曾在 CI 型环境下恒判 unresolved(假红),本机偶然纯 JSON 时才假绿。
// 同时加超时:无界的 execFileSync 会让整道门挂死(实测挂 3 分钟以上)。
const timeout = Number(process.env.SHADOW_CLI_TIMEOUT_MS || 20000)
// win32 上 PATH 里的托管 shim 是 shadow-dev.cmd,而 Node ≥18.20.3/20.12.2 起拒绝在无 shell 下启动 .bat/.cmd(EINVAL)。
// 不显式给 SHADOW_DEV_CLI 时,Windows 必须经 shell 解析命令目录——否则本门在 windows runner 恒 unresolved,
// strict 按设计判红而 ubuntu/macos 全绿(实证:PR #44/#45 的 gate 第 6 步)。降级态依旧自曝,不半绿。
const viaShell = process.platform === 'win32' && !cli
// shell 模式下把命令与参数合成单串、args 留空:既避开 DEP0190(args 与 shell:true 同用只拼接不转义),
// 也保证本门的命令行永远是静态字面量,没有任何外部输入进入 shell。
const bin = viaShell ? `${argv[0]} ${argv[1].join(' ')}` : argv[0]
const binArgs = viaShell ? [] : argv[1]
try {
const out = execFileSync(argv[0], argv[1], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout })
const out = execFileSync(bin, binArgs, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout, shell: viaShell })
const jsonLine = out.split(/\r?\n/).filter(l => l.startsWith('{')).pop()
if (!jsonLine) throw new Error('命令目录无 JSON 契约行')
catalog = Object.keys(JSON.parse(jsonLine).data.commands || {})
Expand Down
Loading
Loading