asia-aidlc 1.14.2 → 1.14.5
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.
- package/.aidlc-engine/cli/flow.py +71 -4
- package/CHANGELOG.md +124 -0
- package/README.md +29 -0
- package/bin/aidlc.js +2 -1
- package/lib/engine.js +29 -16
- package/lib/index.js +16 -5
- package/lib/runner.js +25 -1
- package/package.json +2 -4
- package/.trae/commands/flow-audit.md +0 -13
- package/.trae/commands/flow-branch.md +0 -16
- package/.trae/commands/flow-e2e.md +0 -6
- package/.trae/commands/flow-gate.md +0 -28
- package/.trae/commands/flow-matrix.md +0 -6
- package/.trae/commands/flow-status.md +0 -6
- package/.trae/commands/flow-transition.md +0 -13
- package/.trae/commands/flow-validate.md +0 -8
- package/.trae/hooks/gate_hook.py +0 -334
- package/.trae/hooks.json +0 -60
- package/.trae/mcp.json +0 -11
- package/.trae/rules/flow-orchestration.md +0 -128
- package/.trae/skills/audit-logger/SKILL.md +0 -60
- package/.trae/skills/audit-logger/scripts/append.py +0 -107
- package/.trae/skills/compliance-checker/SKILL.md +0 -40
- package/.trae/skills/compliance-checker/scripts/check.py +0 -32
- package/.trae/skills/design-doc-generator/SKILL.md +0 -38
- package/.trae/skills/gate-verifier/SKILL.md +0 -61
- package/.trae/skills/gate-verifier/scripts/verify.py +0 -228
- package/.trae/skills/observation-doc-generator/SKILL.md +0 -36
- package/.trae/skills/openspec-executor/SKILL.md +0 -61
- package/.trae/skills/release-doc-generator/SKILL.md +0 -38
- package/.trae/skills/render-views/SKILL.md +0 -73
- package/.trae/skills/req-doc-generator/SKILL.md +0 -60
- package/.trae/skills/req-doc-generator/scripts/create_req.py +0 -222
- package/.trae/skills/state-validator/SKILL.md +0 -47
- package/.trae/skills/state-validator/scripts/validate.py +0 -312
- package/.trae/skills/task-split/SKILL.md +0 -114
- package/.trae/skills/task-split/scripts/tasks.py +0 -511
- package/.trae/skills/team-init/SKILL.md +0 -99
- package/.trae/skills/team-init/scripts/init_owners.py +0 -614
- package/.trae/skills/test-manager/SKILL.md +0 -38
- package/.trae/skills/traceability-updater/SKILL.md +0 -34
- package/.trae/skills/traceability-updater/scripts/update.py +0 -98
|
@@ -426,6 +426,8 @@ ROLE_NODE_HINT = (
|
|
|
426
426
|
)
|
|
427
427
|
NODE_LABEL = (("N0", "任务受理"), ("N1", "需求生成"), ("N2", "方案生成"),
|
|
428
428
|
("N3", "代码生成"), ("N4", "测试执行"), ("N5", "交付发布"))
|
|
429
|
+
# 名单里的「角色」是自由文本,但给几个示例用户才知道该写什么(角色会用于自动推断阶段归属)
|
|
430
|
+
ROLE_EXAMPLES = ("产品负责人", "技术负责人", "研发负责人", "QA负责人", "交付负责人")
|
|
429
431
|
|
|
430
432
|
|
|
431
433
|
def parse_members(text):
|
|
@@ -526,6 +528,10 @@ def setup_team_wizard(system, state):
|
|
|
526
528
|
if admin is None:
|
|
527
529
|
return NO_TTY_HINT
|
|
528
530
|
print("团队名单(每行一条,格式 名字:角色;空行结束)")
|
|
531
|
+
print(" 角色示例:" + " / ".join(ROLE_EXAMPLES))
|
|
532
|
+
print(" 也可以写:架构师 / 项目经理 / 测试工程师 / 运维 —— 角色是自由文本,用于自动推断阶段归属")
|
|
533
|
+
print(" 只写名字也行(用这条命令会按名字建成员、阶段归属再单独指定)")
|
|
534
|
+
print(" 例:张敏:产品负责人 / 李强:技术负责人")
|
|
529
535
|
roster = []
|
|
530
536
|
while True:
|
|
531
537
|
try:
|
|
@@ -610,6 +616,8 @@ def next_steps_block(state, team_done=False, hooks_done=False):
|
|
|
610
616
|
lines += [
|
|
611
617
|
"[next] step=team 状态=未配置 影响=门控审批人校验/视图「配置负责人」列/日历按人筛选",
|
|
612
618
|
"[next] step=team 一条命令配好=flow init --team \"张敏:产品负责人,李强:技术负责人\" --admin 张敏",
|
|
619
|
+
"[next] step=team 角色示例=" + " / ".join(ROLE_EXAMPLES) +
|
|
620
|
+
"(角色是自由文本,用于自动推断阶段归属)",
|
|
613
621
|
"[next] step=team 跳过=什么都不做即可(不影响 init 结果;随时可再配)",
|
|
614
622
|
]
|
|
615
623
|
if not hooks_done:
|
|
@@ -626,11 +634,16 @@ def do_init(system, state, skills_dir=".trae/skills", no_skills=False, empty=Fal
|
|
|
626
634
|
# 钩子装到状态文件所在仓库(脚本自身会 git rev-parse 定位仓库根,传目录即可)
|
|
627
635
|
hook_start = os.path.dirname(os.path.abspath(state)) or os.getcwd()
|
|
628
636
|
if os.path.exists(state):
|
|
629
|
-
# 维护模式:已初始化的仓库不能重建状态,但「补装缺失的 Skill /
|
|
637
|
+
# 维护模式:已初始化的仓库不能重建状态,但「补装缺失的 Skill / 钩子 / 刷新引擎」应当仍可执行
|
|
630
638
|
if git_hooks or sync_skills:
|
|
631
639
|
out = "[skip] %s 已存在,不覆盖(维护模式:只执行所请求的安装动作)" % state
|
|
640
|
+
repo_root = _repo_root_from_state(state)
|
|
641
|
+
n_files, eng_dest = _materialize_engine(system, repo_root)
|
|
642
|
+
if n_files is not None:
|
|
643
|
+
system = eng_dest # 后续步骤改用项目内那份引擎
|
|
644
|
+
out += "\nOK: 引擎已落地 → %s(新写/更新 %d 个文件)" % (eng_dest, n_files)
|
|
632
645
|
if sync_skills:
|
|
633
|
-
out += "\nOK: " + _install_skills(system,
|
|
646
|
+
out += "\nOK: " + _install_skills(system, repo_root, skills_dir)
|
|
634
647
|
if git_hooks:
|
|
635
648
|
out += "\nOK: " + install_viewer_hooks(hook_start)
|
|
636
649
|
return out
|
|
@@ -654,6 +667,16 @@ def do_init(system, state, skills_dir=".trae/skills", no_skills=False, empty=Fal
|
|
|
654
667
|
write_state(state, data)
|
|
655
668
|
data_root = os.path.dirname(os.path.abspath(state)) or os.getcwd()
|
|
656
669
|
repo_root = os.path.dirname(data_root) or data_root # .trae/ 等项目配置仍装在仓库根
|
|
670
|
+
# npm 形态:先把引擎落地到项目内,**后续所有步骤都改用项目内那份**——
|
|
671
|
+
# 这样 .trae/mcp.json 的 ${workspaceFolder}/.aidlc-engine/... 与 git 钩子的路径才是真的。
|
|
672
|
+
n_files, eng_dest = _materialize_engine(system, repo_root)
|
|
673
|
+
if n_files is not None:
|
|
674
|
+
system = eng_dest
|
|
675
|
+
engine_note = "OK: 引擎已落地 → %s(新写/更新 %d 个文件)%s" % (
|
|
676
|
+
eng_dest, n_files,
|
|
677
|
+
";之后 Trae MCP 与 git 钩子都指向项目内这份引擎" if n_files else "(已是最新)")
|
|
678
|
+
else:
|
|
679
|
+
engine_note = "OK: 引擎:%s" % eng_dest
|
|
657
680
|
made = _scaffold(system, state)
|
|
658
681
|
trae = _install_trae(system, repo_root)
|
|
659
682
|
rule = _install_agent_rule(system, repo_root)
|
|
@@ -668,10 +691,10 @@ def do_init(system, state, skills_dir=".trae/skills", no_skills=False, empty=Fal
|
|
|
668
691
|
dep = "提示: 未安装 jsonschema,validate 将降级运行(pip install jsonschema 启用完整 Schema 校验)"
|
|
669
692
|
prefix = "(空白,无示例实体)" if empty else ""
|
|
670
693
|
out = ("OK: 已从 example 生成 %s%s(%d 需求 / %d EPIC / %d WU / %d CHG / %d REL)\n"
|
|
671
|
-
"
|
|
694
|
+
"%s\nOK: %s\nOK: Trae 接入: %s\nOK: %s\nOK: %s" % (
|
|
672
695
|
state, prefix, len(data.get("requirements", [])), len(data.get("epics", [])),
|
|
673
696
|
len(data.get("workunits", [])), len(data.get("changes", [])), len(data.get("releases", [])),
|
|
674
|
-
made, trae, rule, sk))
|
|
697
|
+
engine_note, made, trae, rule, sk))
|
|
675
698
|
if git_hooks:
|
|
676
699
|
out += "\nOK: " + install_viewer_hooks(hook_start)
|
|
677
700
|
if dep:
|
|
@@ -730,6 +753,50 @@ def do_init(system, state, skills_dir=".trae/skills", no_skills=False, empty=Fal
|
|
|
730
753
|
return out
|
|
731
754
|
|
|
732
755
|
|
|
756
|
+
def _materialize_engine(system, repo_root):
|
|
757
|
+
"""把「包里的引擎」落地到项目内 `.aidlc-engine/`(npm 安装形态专用,仓库内形态是 no-op)。
|
|
758
|
+
|
|
759
|
+
为什么必须做:npm 形态下引擎只存在于 `node_modules/asia-aidlc/.aidlc-engine/`,
|
|
760
|
+
而项目侧的接入件全都按**项目内**路径引用它——
|
|
761
|
+
· `.trae/mcp.json` → `${workspaceFolder}/.aidlc-engine/cli/flow_mcp.py`
|
|
762
|
+
· git 钩子 → `python .aidlc-engine/viewer/render.py --if-stale`
|
|
763
|
+
· `.trae/rules/flow-orchestration.md` → `.aidlc-engine/cli/flow.py`
|
|
764
|
+
不落地的话,这三样在项目里全是**死路径**(MCP 起不来、钩子空跑)。
|
|
765
|
+
|
|
766
|
+
幂等:逐文件比较,只写缺失或内容不同的;跳过 `__pycache__` / `*.pyc` / `.tmp*`。
|
|
767
|
+
返回 (落地文件数, 目标路径) 或 (None, 原因)。
|
|
768
|
+
"""
|
|
769
|
+
src = (os.environ.get("AIDLC_ENGINE_SRC") or "").strip()
|
|
770
|
+
if not src:
|
|
771
|
+
return None, "(仓库内形态:引擎已在项目里,无需落地)"
|
|
772
|
+
if not os.path.isdir(src):
|
|
773
|
+
return None, "找不到包内引擎 %s" % src
|
|
774
|
+
dest = os.path.join(repo_root, ".aidlc-engine")
|
|
775
|
+
if os.path.abspath(src) == os.path.abspath(dest):
|
|
776
|
+
return None, "(引擎与项目同一目录,无需落地)"
|
|
777
|
+
written = skipped = 0
|
|
778
|
+
for root, dirs, files in os.walk(src):
|
|
779
|
+
dirs[:] = [d for d in dirs if d != "__pycache__" and not d.startswith(".tmp")]
|
|
780
|
+
rel = os.path.relpath(root, src)
|
|
781
|
+
target_dir = dest if rel == "." else os.path.join(dest, rel)
|
|
782
|
+
os.makedirs(target_dir, exist_ok=True)
|
|
783
|
+
for name in files:
|
|
784
|
+
if name.endswith((".pyc", ".pyo")) or name.startswith(".tmp"):
|
|
785
|
+
continue
|
|
786
|
+
s, d = os.path.join(root, name), os.path.join(target_dir, name)
|
|
787
|
+
try:
|
|
788
|
+
if os.path.exists(d) and os.path.getsize(d) == os.path.getsize(s):
|
|
789
|
+
with open(s, "rb") as fa, open(d, "rb") as fb:
|
|
790
|
+
if fa.read() == fb.read():
|
|
791
|
+
skipped += 1
|
|
792
|
+
continue
|
|
793
|
+
shutil.copy2(s, d)
|
|
794
|
+
written += 1
|
|
795
|
+
except OSError as exc:
|
|
796
|
+
LOG.warning("引擎落地失败 %s:%s", rel, exc)
|
|
797
|
+
return written, dest
|
|
798
|
+
|
|
799
|
+
|
|
733
800
|
def _scaffold(system, state):
|
|
734
801
|
"""按目录规约创建仓库骨架:aidlc-data/ 产物目录 + 登记册 + 仓库根 .gitignore 合并。全部幂等,已存在即跳过。"""
|
|
735
802
|
data_root = os.path.dirname(os.path.abspath(state)) or os.getcwd()
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,130 @@
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## v1.25 · 2026-10-09 — npm 形态 init 只落 `aidlc-data/`:引擎与 `.trae/` 现在真的落地
|
|
9
|
+
|
|
10
|
+
### 背景(用户报障)
|
|
11
|
+
|
|
12
|
+
> 「从 npm 上安装完这个包之后进行初始化,只会生成 `aidlc-data` 这个文件夹,而 `.aidlc-engine`、`.trae` 没有生成,这几个也很重要」
|
|
13
|
+
|
|
14
|
+
用户是对的,而且这是**同一个根因**的两个症状。
|
|
15
|
+
|
|
16
|
+
### 根因(实测量化)
|
|
17
|
+
|
|
18
|
+
npm 形态的挂载表只有两个子目录,**项目根本身没挂**:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
mounts: /project/.aidlc-engine ← 包里的引擎(NODEFS)
|
|
22
|
+
/project/aidlc-data ← 项目数据(NODEFS,mustExist:false 时创建)
|
|
23
|
+
/project 本身 = MEMFS ← 内存盘!
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
于是 `flow init` 打印了 `OK: Trae 接入: … → .trae/`「技能 14 个 → .trae/skills」,
|
|
27
|
+
**写入全落在内存盘 `/project/.trae`,进程退出即丢**;复现脚本在宿主上只看到 `aidlc-data/`,
|
|
28
|
+
`.trae` 与 `.aidlc-engine` 均为 false。而 `.aidlc-engine/` 属于"引擎来自包"的既有设计,**从未被复制**。
|
|
29
|
+
|
|
30
|
+
致命连锁:即使 `.trae/` 落了,里面的路径也全是死的——
|
|
31
|
+
`.trae/mcp.json` → `${workspaceFolder}/.aidlc-engine/cli/flow_mcp.py`(不存在)、
|
|
32
|
+
git 钩子 → `python .aidlc-engine/viewer/render.py --if-stale`(不存在)、
|
|
33
|
+
`.trae/rules/flow-orchestration.md` → `.aidlc-engine/cli/flow.py`(不存在)。
|
|
34
|
+
|
|
35
|
+
### 变更明细
|
|
36
|
+
|
|
37
|
+
1. **`lib/engine.js` 挂载表重做**:npm 形态改为
|
|
38
|
+
`{host: <项目根>, guest: "/project"}`(NODEFS,写入直达宿主)+
|
|
39
|
+
`{host: <包内引擎>, guest: "/engine-src"}`(`ENGINE_SRC_GUEST`),
|
|
40
|
+
`resolveMounts` 返回值新增 `engineSrc`。
|
|
41
|
+
2. **`lib/index.js`**:新增 `guestPath` 二选一规则——项目内**已有** `.aidlc-engine/cli/flow.py`
|
|
42
|
+
就用 `/project/.aidlc-engine/…`(与 Trae 钩子/MCP 同一份引擎,避免版本错位),
|
|
43
|
+
否则用 `/engine-src/…`(首次 init 前必须能跑起来);并通过环境变量 `AIDLC_ENGINE_SRC` 告知 Python 侧。
|
|
44
|
+
3. **`flow.py` 新增 `_materialize_engine()`**:把 `/engine-src` 逐文件复制到 `<项目>/.aidlc-engine/`,
|
|
45
|
+
跳过 `__pycache__`/`*.pyc`/`.tmp*`,**幂等**(同尺寸先比内容,只写有变化的);
|
|
46
|
+
init 之后 `system` 切到项目内那份,于是 `.trae/mcp.json`、git 钩子、`.trae/skills` 的路径全部指向项目内引擎。
|
|
47
|
+
**维护模式也刷新引擎**(`flow init --sync-skills` 可用于 `npm update` 后同步)。
|
|
48
|
+
4. **`lib/runner.js` 修一个既有 bug**:`ensureMounts` 无条件重复 `FS.mount`,
|
|
49
|
+
同一 Node 进程里**第二次** `runScript` 会抛 `ErrnoError`(我的验证脚本是第一个连调两次的用例)。
|
|
50
|
+
改为记录每个 guest 当前挂的宿主:同宿主跳过,不同宿主先 `unmount` 再挂(支持一个进程里先后操作不同项目)。
|
|
51
|
+
5. **`bin/aidlc.js`**:`doctor` 增加一行「项目内引擎 … OK / 未落地(先 aidlc flow init)」;
|
|
52
|
+
`项目根` 那行措辞改为「npm 形态:引擎来自包,`flow init` 会落地到项目内」。
|
|
53
|
+
6. **`README.md` 新增「从 npm 安装到自己的项目」**:三样落地物各是什么、为什么 `.aidlc-engine/` 必须落地、
|
|
54
|
+
升级后用 `--sync-skills` 刷新,以及上面那段"实现要点(改引擎挂载时必看)"。此前 README **完全没写** npm 形态,
|
|
55
|
+
这正是这个坑长期没被发现的原因。
|
|
56
|
+
7. **`lib/engine.js` 头注释**同步(原文还写着"用 MEMFS 造一个合成项目根")。
|
|
57
|
+
|
|
58
|
+
### 验证(合成根形态,实跑)
|
|
59
|
+
|
|
60
|
+
7 组断言全过:
|
|
61
|
+
- ① `init --empty --no-input --git-hooks` 退出码 0;
|
|
62
|
+
- ② 宿主上 **`.aidlc-engine/`(91 个文件)+ `.trae/` + `aidlc-data/`** 三者全部存在;
|
|
63
|
+
- ③ `.trae/mcp.json` 的 `args` 解出来是**真实存在**的文件;
|
|
64
|
+
- ④ git 钩子内容是 `.aidlc-engine/viewer/render.py`,且**不泄漏** `/engine-src`;
|
|
65
|
+
- ⑤ 后续 `flow status` 改用项目内引擎(不再报 ErrnoError);
|
|
66
|
+
- ⑥ `flow init --sync-skills` 幂等刷新(第二次「新写/更新 **0** 个文件」);
|
|
67
|
+
- ⑦ `flow view` 用项目内 `render.py` 产出日历。
|
|
68
|
+
|
|
69
|
+
回归:`npm test` SMOKE PASS;仓库内形态(CPython)`flow init` 仍写 `.trae/` + `aidlc-data/`,
|
|
70
|
+
且**不会**多建一份 `.aidlc-engine/`(正确 no-op,输出「(仓库内形态:引擎已在项目里,无需落地)」)。
|
|
71
|
+
|
|
72
|
+
### 说明
|
|
73
|
+
|
|
74
|
+
- 落地后项目就是标准两目录布局,`git status` 会看到 `.aidlc-engine/` 与 `.trae/` 作为新增内容——这是**预期**的,
|
|
75
|
+
它们是项目侧接入件,应当提交(派生视图仍在 `.gitignore` 里)。
|
|
76
|
+
- Trae 的 MCP server 仍是 `python <项目>/.aidlc-engine/cli/flow_mcp.py`,**需要本机有 Python**;
|
|
77
|
+
`aidlc …` 命令本身走内置 Pyodide、不需要(这是既有设计,本次未改)。
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## v1.24 · 2026-10-09 — 团队名单给出角色示例 + 修 `.npmignore` 夹带仓库自身 `.trae/`
|
|
82
|
+
|
|
83
|
+
### 背景(用户报障)
|
|
84
|
+
|
|
85
|
+
1. **团队初始化时没有角色示例**:「后面都不知道要写什么角色」——向导只写了「格式 名字:角色」,
|
|
86
|
+
没告诉用户角色长什么样。
|
|
87
|
+
2. **「初始化之后原本的 `.trae` 文件夹没了,这个很重要」**——查清了机制,见下。
|
|
88
|
+
|
|
89
|
+
### 变更明细
|
|
90
|
+
|
|
91
|
+
- **团队名单提示补角色示例**(向导 + `[next]` 块 + 常量 `ROLE_EXAMPLES`):
|
|
92
|
+
```
|
|
93
|
+
团队名单(每行一条,格式 名字:角色;空行结束)
|
|
94
|
+
角色示例:产品负责人 / 技术负责人 / 研发负责人 / QA负责人 / 交付负责人
|
|
95
|
+
也可以写:架构师 / 项目经理 / 测试工程师 / 运维 —— 角色是自由文本,用于自动推断阶段归属
|
|
96
|
+
只写名字也行(按名字建成员、阶段归属再单独指定)
|
|
97
|
+
例:张敏:产品负责人 / 李强:技术负责人
|
|
98
|
+
```
|
|
99
|
+
Agent 路径的 `[next] step=team 角色示例=…` 同样带上,避免 Agent 在对话里也不知道该引导用户写什么。
|
|
100
|
+
|
|
101
|
+
- **`.npmignore` 修两处夹带**:
|
|
102
|
+
- **`/.trae/`**:仓库自己的 Trae 接入件(`.trae/` 是 `flow init` 装到**项目侧**的派生件)被打进了包。
|
|
103
|
+
实测 **1.14.0 顶层 `.trae` 条目 = 0,1.14.1/1.14.2 变成 34**(因为仓库里出现 `.trae/` 后它不再被排除)。
|
|
104
|
+
修后 `npm pack` **entryCount 87**、无顶层 `.trae`。
|
|
105
|
+
- **`/.tmp*`(不带斜杠)**:原来的 `/.tmp*/` 只匹配**目录**,临时**文件**(如 `.tmp-xxx.js`)仍会被打进包——
|
|
106
|
+
实测修复前的包里就有这个文件。
|
|
107
|
+
|
|
108
|
+
### 关于「`.trae` 没了」的排查结论(未改策略,待定)
|
|
109
|
+
|
|
110
|
+
- **`flow init` 不会删 `.trae`**:实测预置 4 个哨兵文件(`mcp.json` + 自有 command/rule/skill)后跑 init,
|
|
111
|
+
**哨兵全在**,`mcp.json` 是**合并**(用户 server + flow server 都在),总条目由少变多。
|
|
112
|
+
- **真正机制**:本仓库 `.gitignore` **第 34 行就是 `.trae`**(落在「IDE / 编辑器」区块,`.idea/`、`.vscode/` 旁边)
|
|
113
|
+
→ `.trae/` **不被 git 跟踪** → `git clean -xfd`、**新克隆**、部分 checkout 都会**静默删掉它**。
|
|
114
|
+
- **文件自相矛盾**:同一个 `.gitignore` 第 69 行注释却写着「明确保留(不忽略)→ `.trae/` Trae 接入件」。
|
|
115
|
+
两者必有一错。
|
|
116
|
+
- **用户项目不受影响** ✓:`templates/gitignore.example`(`flow init` 写给用户项目的那份)与
|
|
117
|
+
`GITIGNORE_ENTRIES` **都不含 `.trae`**,所以只有本框架仓库有这个坑。
|
|
118
|
+
- 策略二选一(需拍板):**(A) 取消忽略**,让 `.trae/` 可提交、可随克隆恢复、跨机器共享(代价:14 个 Skill
|
|
119
|
+
是 `.aidlc-engine/skills/` 的副本,提交会重复且可能漂移);**(B) 保持忽略**(承认它是派生件),
|
|
120
|
+
同时把第 69 行那条矛盾的注释改对,并在 init 输出里提示「`.trae/` 是派生件,被 clean/新克隆清掉后重跑本命令即可恢复」。
|
|
121
|
+
倾向 (B)——它本就是 `flow init` 从 `.aidlc-engine/cli/.trae/` + skills 装出来的派生件。
|
|
122
|
+
|
|
123
|
+
### 验证
|
|
124
|
+
|
|
125
|
+
- 向导输出含全部 5 个角色示例 + 「自由文本 / 可只写名字」说明(7 项断言全过);`[next]` 块含 `角色示例=`。
|
|
126
|
+
- `npm pack --json`:`entryCount: 87`,无 `package/.trae/**`、无 `.tmp*`;`.aidlc-engine/cli/.trae/**` 10 个条目仍在
|
|
127
|
+
(它是**源模板**,必须随包分发,否则装完无法生成 `.trae`)。
|
|
128
|
+
- 预置 `.trae` 后跑 init:哨兵 4/4 存活、`mcp.json` 合并保留用户 server。
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
8
132
|
## v1.23 · 2026-10-09 — `flow init` 收尾主动询问是否配团队(并修掉 npm 形态下的"假提示")
|
|
9
133
|
|
|
10
134
|
### 背景
|
package/README.md
CHANGED
|
@@ -124,6 +124,35 @@ python .aidlc-engine/cli/flow.py code verify --chg CHG-102 # 代
|
|
|
124
124
|
3. 编排 Agent 提示词:Trae 环境由 init 自动写入 `.trae/rules/flow-orchestration.md`(alwaysApply);其他载体手工加载 `agent/orchestration-agent-system-prompt.md`。
|
|
125
125
|
4. 按 `README.md` 的"落地三步"接入第一个真实需求,跑通一条完整链路。
|
|
126
126
|
|
|
127
|
+
### 从 npm 安装到自己的项目
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm i -D asia-aidlc # 或全局:npm i -g asia-aidlc(之后可直接 aidlc …,无需 npx)
|
|
131
|
+
npx aidlc flow init # 在你自己的项目根执行
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`flow init` 会把三样东西**落到你的项目里**(不是只留在 `node_modules/`):
|
|
135
|
+
|
|
136
|
+
| 落地物 | 作用 |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `.aidlc-engine/` | 引擎(从包里复制过来)。**必须落地的原因**:`.trae/mcp.json` 指向 `${workspaceFolder}/.aidlc-engine/cli/flow_mcp.py`、git 钩子跑 `.aidlc-engine/viewer/render.py`——项目里没有引擎,这两条都是死路径(MCP 起不来、钩子空跑) |
|
|
139
|
+
| `.trae/` | Trae 接入件:MCP server、8 个斜杠命令、14 个技能、编排规则(`rules/flow-orchestration.md`) |
|
|
140
|
+
| `aidlc-data/` | 流程数据(状态真相 + 需求/设计/测试/发布产物) |
|
|
141
|
+
|
|
142
|
+
落完之后项目就是标准的**两目录布局**,`.trae/` 与 `.aidlc-engine/` 都该进版本控制
|
|
143
|
+
(`flow init` 写入的 `.gitignore` 只忽略派生视图 `aidlc-data/*.html`、`aidlc-data/req/` 等,不忽略它们)。
|
|
144
|
+
|
|
145
|
+
升级包之后刷新项目内引擎(幂等:只写内容有变化的文件):
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx aidlc flow init --sync-skills # 维护模式:不覆盖状态,只刷新引擎 + 补装缺失 Skill
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
> 实现要点(改引擎挂载时必看):npm 形态下**项目根本身**被挂到虚拟 FS 的 `/project`(NODEFS,写入直达宿主),
|
|
152
|
+
> 包里的引擎另挂到 `/engine-src`;`flow init` 把 `/engine-src` 落地成 `<项目>/.aidlc-engine/`,
|
|
153
|
+
> 之后所有命令改用项目内那份。早期只挂了 `aidlc-data` 与包内引擎两个子目录,`/project` 是内存盘,
|
|
154
|
+
> 于是 `.trae/` 写进了内存、进程退出即丢——**宿主上只剩 `aidlc-data/`**。
|
|
155
|
+
|
|
127
156
|
## 验证清单(用户后续验收用)
|
|
128
157
|
|
|
129
158
|
- [ ] `state-validator` 对 example.json 返回 EXIT=0
|
package/bin/aidlc.js
CHANGED
|
@@ -109,8 +109,9 @@ async function cmdDoctor() {
|
|
|
109
109
|
const lines = [];
|
|
110
110
|
lines.push(`Node ${process.version}`);
|
|
111
111
|
lines.push(`包根 ${st.packageRoot}`);
|
|
112
|
-
lines.push(`项目根 ${st.projectRoot}${st.synthetic ? "
|
|
112
|
+
lines.push(`项目根 ${st.projectRoot}${st.synthetic ? "(npm 形态:引擎来自包,flow init 会落地到项目内)" : "(仓库内形态)"}`);
|
|
113
113
|
lines.push(`引擎 ${st.engineHost} ${fs.existsSync(st.engineHost) ? "OK" : "缺失"}`);
|
|
114
|
+
lines.push(`项目内引擎 ${path.join(st.projectRoot, ".aidlc-engine")} ${fs.existsSync(path.join(st.projectRoot, ".aidlc-engine", "cli", "flow.py")) ? "OK" : "未落地(先 aidlc flow init)"}`);
|
|
114
115
|
lines.push(`数据目录 ${path.join(st.projectRoot, "aidlc-data")} ${st.hasData ? "OK" : "不存在(先 aidlc flow init)"}`);
|
|
115
116
|
let pyVer = "未加载";
|
|
116
117
|
try {
|
package/lib/engine.js
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
* engine.js —— 引擎侧定位:包根、项目根、Skill 清单、Pyodide 可用性。
|
|
3
3
|
*
|
|
4
4
|
* 两种部署形态(同一套代码):
|
|
5
|
-
* A. 仓库内(本仓库就是项目):包根 ===
|
|
6
|
-
* B. npm 安装(node_modules/asia-aidlc
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* A. 仓库内(本仓库就是项目):包根 === 项目根,整目录挂到 /project。
|
|
6
|
+
* B. npm 安装(node_modules/asia-aidlc):项目根本身挂到 /project(NODEFS,写入直达宿主),
|
|
7
|
+
* 包里的引擎另挂到 /engine-src;`flow init` 会把 /engine-src **落地**成项目内的
|
|
8
|
+
* `.aidlc-engine/`,之后再跑命令就改用项目内那份(环境变量 AIDLC_ENGINE_SRC 由 lib/index.js 注入)。
|
|
9
|
+
*
|
|
10
|
+
* 于是两种形态最终都是同一个「两目录布局」(`<项目>/.aidlc-engine/` + `<项目>/aidlc-data/`),
|
|
11
|
+
* 脚本里所有基于 __file__ 的路径解析都落到 /project,**无需改一行 Python**。
|
|
9
12
|
*/
|
|
10
13
|
const fs = require("fs");
|
|
11
14
|
const path = require("path");
|
|
@@ -92,14 +95,22 @@ const NEEDS_SUBPROCESS = [
|
|
|
92
95
|
"cli_flow.py",
|
|
93
96
|
];
|
|
94
97
|
|
|
98
|
+
/** npm 形态下「包里的引擎」挂载点:`flow init` 从这里复制到项目内的 .aidlc-engine/。 */
|
|
99
|
+
const ENGINE_SRC_GUEST = "/engine-src";
|
|
100
|
+
|
|
95
101
|
function hasSubprocessDependency(relPath) {
|
|
96
102
|
const p = String(relPath).split(path.sep).join("/");
|
|
97
103
|
return NEEDS_SUBPROCESS.some((x) => p.endsWith(x));
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
/**
|
|
101
|
-
* 计算挂载表。返回 { synthetic, mounts:[{host, guest, mustExist}], cwd, projectRoot }。
|
|
102
|
-
* synthetic=true 表示
|
|
107
|
+
* 计算挂载表。返回 { synthetic, mounts:[{host, guest, mustExist}], cwd, projectRoot, engineSrc }。
|
|
108
|
+
* synthetic=true 表示 npm 安装形态(引擎来自包,需要 `flow init` 落地到项目内)。
|
|
109
|
+
*
|
|
110
|
+
* ⚠️ 关键:npm 形态下**必须把项目根本身**挂到 /project。
|
|
111
|
+
* 早期只挂了 `/project/.aidlc-engine`(包里的引擎)与 `/project/aidlc-data` 两个子目录,
|
|
112
|
+
* `/project` 本身是 MEMFS 内存盘 —— 于是 `flow init` 写出的 `.trae/`(以及任何非 aidlc-data 的路径)
|
|
113
|
+
* 都写进了内存、进程退出即丢:实测宿主上只留下 `aidlc-data/`,`.trae` 和 `.aidlc-engine` 都不存在。
|
|
103
114
|
*/
|
|
104
115
|
function resolveMounts(packageRoot, projectRoot) {
|
|
105
116
|
const engineHost = path.join(packageRoot, ENGINE_DIR);
|
|
@@ -109,23 +120,25 @@ function resolveMounts(packageRoot, projectRoot) {
|
|
|
109
120
|
synthetic: false,
|
|
110
121
|
projectRoot,
|
|
111
122
|
cwd: "/project",
|
|
123
|
+
engineSrc: null, // 仓库内形态:引擎就在 /project/.aidlc-engine
|
|
112
124
|
mounts: [{ host: projectRoot, guest: "/project", mustExist: true }],
|
|
113
125
|
};
|
|
114
126
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
return {
|
|
128
|
+
synthetic: true,
|
|
129
|
+
projectRoot,
|
|
130
|
+
cwd: "/project",
|
|
131
|
+
engineSrc: ENGINE_SRC_GUEST, // 包里的引擎:供 `flow init` 复制到项目内
|
|
132
|
+
mounts: [
|
|
133
|
+
{ host: projectRoot, guest: "/project", mustExist: true },
|
|
134
|
+
{ host: engineHost, guest: ENGINE_SRC_GUEST, mustExist: true },
|
|
135
|
+
],
|
|
136
|
+
};
|
|
125
137
|
}
|
|
126
138
|
|
|
127
139
|
module.exports = {
|
|
128
140
|
ENGINE_DIR,
|
|
141
|
+
ENGINE_SRC_GUEST,
|
|
129
142
|
PROJECT_SUBDIRS,
|
|
130
143
|
findPackageRoot,
|
|
131
144
|
findProjectRoot,
|
package/lib/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* const r = await runSkill("team-init", ["init", "--project", "订单中心", "--admin", "张敏"]);
|
|
6
6
|
* console.log(r.code, r.stdout);
|
|
7
7
|
*/
|
|
8
|
+
const fs = require("fs");
|
|
8
9
|
const path = require("path");
|
|
9
10
|
const eng = require("./engine");
|
|
10
11
|
const { runPython } = require("./runner");
|
|
@@ -12,14 +13,24 @@ const { runPython } = require("./runner");
|
|
|
12
13
|
function projectContext(opts = {}) {
|
|
13
14
|
const packageRoot = opts.packageRoot || eng.findPackageRoot(__dirname);
|
|
14
15
|
const projectRoot = opts.projectRoot || eng.findProjectRoot(opts.cwd || process.cwd());
|
|
15
|
-
const { synthetic, mounts, cwd } = eng.resolveMounts(packageRoot, projectRoot);
|
|
16
|
-
return { packageRoot, projectRoot, synthetic, mounts, cwd };
|
|
16
|
+
const { synthetic, mounts, cwd, engineSrc } = eng.resolveMounts(packageRoot, projectRoot);
|
|
17
|
+
return { packageRoot, projectRoot, synthetic, mounts, cwd, engineSrc };
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* 把引擎相对路径(如 .aidlc-engine/skills/x/scripts/y.py)映射为虚拟 FS 路径。
|
|
22
|
+
*
|
|
23
|
+
* npm 形态下项目根与包引擎是**两个挂载点**,要分两种情形:
|
|
24
|
+
* · 项目内**已有**引擎(`flow init` 落地之后)→ 用 `/project/.aidlc-engine/…`,
|
|
25
|
+
* 与项目里 Trae 钩子/MCP 用同一份引擎,避免版本错位;
|
|
26
|
+
* · 项目内**还没有**引擎(首次 init 之前)→ 用包里的 `/engine-src/…`,否则连 init 都跑不起来。
|
|
27
|
+
*/
|
|
20
28
|
function guestPath(ctx, relPath) {
|
|
21
29
|
const rel = String(relPath).split(path.sep).join("/").replace(/^\.\//, "");
|
|
22
|
-
if (ctx.synthetic
|
|
30
|
+
if (ctx.synthetic && ctx.engineSrc && rel.startsWith(eng.ENGINE_DIR + "/")) {
|
|
31
|
+
const inProject = fs.existsSync(path.join(ctx.projectRoot, eng.ENGINE_DIR, "cli", "flow.py"));
|
|
32
|
+
if (!inProject) return ctx.engineSrc + "/" + rel.slice(eng.ENGINE_DIR.length + 1);
|
|
33
|
+
}
|
|
23
34
|
return "/project/" + rel;
|
|
24
35
|
}
|
|
25
36
|
|
|
@@ -34,7 +45,7 @@ async function runScript(relPath, args = [], opts = {}) {
|
|
|
34
45
|
args,
|
|
35
46
|
mounts: ctx.mounts,
|
|
36
47
|
cwd: ctx.cwd,
|
|
37
|
-
env: { AIDLC_NPM: "1", ...(opts.env || {}) },
|
|
48
|
+
env: { AIDLC_NPM: "1", AIDLC_ENGINE_SRC: ctx.engineSrc || "", ...(opts.env || {}) },
|
|
38
49
|
onStdout: opts.onStdout,
|
|
39
50
|
onStderr: opts.onStderr,
|
|
40
51
|
indexURL: opts.indexURL,
|
package/lib/runner.js
CHANGED
|
@@ -61,17 +61,41 @@ if "subprocess" not in sys.modules:
|
|
|
61
61
|
sys.modules["subprocess"] = _m
|
|
62
62
|
`;
|
|
63
63
|
|
|
64
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* 把宿主目录挂进虚拟 FS(NODEFS 直通)。挂载点若在 MEMFS 里则就地建目录,不碰宿主磁盘。
|
|
66
|
+
*
|
|
67
|
+
* 同一 runtime 会被多次复用(同一个 Node 进程里连调两次 runScript),而 Emscripten 对
|
|
68
|
+
* **已挂载**的挂载点再 mount 会抛 ErrnoError —— 所以这里记住每个 guest 当前挂的是哪个宿主:
|
|
69
|
+
* · 同一个宿主 → 跳过(这是同进程多命令的常态)
|
|
70
|
+
* · 不同宿主 → 先 unmount 再挂(允许一个进程里先后操作不同项目)
|
|
71
|
+
*/
|
|
72
|
+
const mountedHosts = new WeakMap(); // py → { guest: host }
|
|
73
|
+
|
|
65
74
|
function ensureMounts(py, mounts) {
|
|
75
|
+
let state = mountedHosts.get(py);
|
|
76
|
+
if (!state) {
|
|
77
|
+
state = {};
|
|
78
|
+
mountedHosts.set(py, state);
|
|
79
|
+
}
|
|
66
80
|
for (const m of mounts) {
|
|
67
81
|
if (m.mustExist !== false && !fs.existsSync(m.host)) {
|
|
68
82
|
throw new Error(`挂载源不存在:${m.host}`);
|
|
69
83
|
}
|
|
84
|
+
if (state[m.guest] === m.host) continue;
|
|
85
|
+
if (state[m.guest]) {
|
|
86
|
+
try {
|
|
87
|
+
py.FS.unmount(m.guest);
|
|
88
|
+
} catch (e) {
|
|
89
|
+
/* 缓存与实际不一致时忽略,下面照常挂 */
|
|
90
|
+
}
|
|
91
|
+
delete state[m.guest];
|
|
92
|
+
}
|
|
70
93
|
const parent = path.posix.dirname(m.guest);
|
|
71
94
|
if (parent !== "/" && !py.FS.analyzePath(parent).exists) py.FS.mkdirTree(parent);
|
|
72
95
|
if (!py.FS.analyzePath(m.guest).exists) py.FS.mkdir(m.guest);
|
|
73
96
|
if (m.mustExist === false && !fs.existsSync(m.host)) fs.mkdirSync(m.host, { recursive: true });
|
|
74
97
|
py.FS.mount(py.FS.filesystems.NODEFS, { root: m.host }, m.guest);
|
|
98
|
+
state[m.guest] = m.host;
|
|
75
99
|
}
|
|
76
100
|
}
|
|
77
101
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "asia-aidlc",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.5",
|
|
4
4
|
"description": "OpenSpec + AI-DLC 研发流程能力包:14 个 Skill(SKILL.md + 确定性 Python 脚本)、flow CLI、状态机真相源、研发日历 / 交付控制台 / 追溯图谱三个静态视图。内置 Pyodide 运行时,装完即可执行 Python 脚本,无需本机安装 Python。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aidlc",
|
|
@@ -30,8 +30,6 @@
|
|
|
30
30
|
"test:viewer": "node test/viewer-js.js"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"
|
|
34
|
-
"pyodide": "^314.0.7",
|
|
35
|
-
"validate": "^5.2.0"
|
|
33
|
+
"pyodide": "^314.0.7"
|
|
36
34
|
}
|
|
37
35
|
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: flow-audit
|
|
3
|
-
description: 追加审计日志(append-only,action 必须属于封闭事件注册表)
|
|
4
|
-
argument-hint: --actor <执行者> --action <注册表事件> --entity <实体> --result ok|blocked|failed
|
|
5
|
-
---
|
|
6
|
-
追加一条审计日志。参数:--actor(人或Agent)、--action、--entity(如 REQ-100)、--result(ok/blocked/failed)、--detail(可选)。
|
|
7
|
-
若已配置 flow MCP,调用 mcp__flow__flow_audit 工具;否则运行 `python .aidlc-engine/cli/flow.py audit --actor ... --action ... --entity ... --result ...`。
|
|
8
|
-
|
|
9
|
-
硬性规则:
|
|
10
|
-
- action 必须属于 `.aidlc-engine/state/audit-events.json` 封闭注册表(25 事件,含 emitter 归属);未注册事件会被类型化拒绝([GUARD_EVENT_NOT_REGISTERED]),禁止先使用再补注册。
|
|
11
|
-
- 只增不删,seq 严格连续递增;禁止改历史条目。
|
|
12
|
-
- 审计先行:状态变更的审计事件必须先于(或与状态变更同一次原子写中)写入——状态迁移/门控请优先用 `flow transition` / `flow gate --record`,它们已内置审计先行。
|
|
13
|
-
- 新增事件类型:先改 audit-events.json(desc + emitter)+ .aidlc-engine/templates/audit-log.md,再使用。
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: flow-branch
|
|
3
|
-
description: 执行分支协作——建/查/合并/丢弃 Change 执行分支(feature/chg-<NNN>)
|
|
4
|
-
---
|
|
5
|
-
管理 Change 的 git 执行分支(分支归属制,CHG 实施强制前置):
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
python .aidlc-engine/cli/flow.py branch create --chg CHG-xxx # 建执行分支(前置:CHG=proposed、WU gate 已过、当前在 main、工作区干净)
|
|
9
|
-
python .aidlc-engine/cli/flow.py branch status # 只读查看各 CHG 分支归属与当前 git 分支
|
|
10
|
-
python .aidlc-engine/cli/flow.py branch merge --chg CHG-xxx # code_review 留痕 + trailer 预检 + ff-only 合并回 main(代码冻结)
|
|
11
|
-
python .aidlc-engine/cli/flow.py branch discard --chg CHG-xxx # 丢弃执行分支、归属回 main(状态回退另走 transition)
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
若已配置 flow MCP,优先直接调用 `mcp__flow__flow_branch_create` / `flow_branch_merge` / `flow_branch_discard` 工具;未配置则运行上面的 CLI 命令。
|
|
15
|
-
|
|
16
|
-
**顺序铁律**:建分支 → `flow transition <CHG-ID> in_progress` → 分支内实施 → code_review 门留痕 → merge(或 discard 回退)→ `flow transition <CHG-ID> archived`。CHG 状态变更一律走 `flow transition`,禁止直接改 `workflow-state.json`。
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: flow-gate
|
|
3
|
-
description: 执行门控校验并留痕(req_review / workunit_gate / release_gate),两步确认(--request → 人工确认 → --record),支持冻结 hash 与 HUMAN_TURN 证据核验
|
|
4
|
-
argument-hint: <实体ID> <门控类型> [--request|--record|--reject] [--reviewer <人>] [--confirm <人>] [--note <说明>]
|
|
5
|
-
---
|
|
6
|
-
对指定实体执行门控(两步确认,P1-8)。参数:$1=实体ID(如 WU-03 / REL-100),$2=门控类型(req_review / workunit_gate / release_gate)。
|
|
7
|
-
|
|
8
|
-
若已配置 flow MCP,调用 mcp__flow__flow_gate 工具;否则运行 `python .aidlc-engine/cli/flow.py gate $1 $2 ...`。
|
|
9
|
-
|
|
10
|
-
**第一步(开启门禁)**:`flow_gate(request=true)` / `flow gate $1 $2 --request` 登记 pending 待确认请求。
|
|
11
|
-
|
|
12
|
-
**HARD STOP 铁律(登记后立即执行)**:
|
|
13
|
-
- 向用户完整呈现:门禁检查单逐项结论 + 确认问题(批准 / 修改 / 驳回 三选项)。
|
|
14
|
-
- 呈现后**立即结束回合等待用户回复**;未获用户回复前不得调用任何工具、不得自行推进。
|
|
15
|
-
(Trae hooks 强制此仪式:enforce-gate 阻断一切非只读工具,stop 阻断未提问的收尾。)
|
|
16
|
-
|
|
17
|
-
**第二步(人工决策后留痕)**:
|
|
18
|
-
- 用户批准 → `flow_gate(record=true, confirm=<用户/确认人>)` / `flow gate $1 $2 --record --confirm <确认人>`。
|
|
19
|
-
CLI 核验存在 ts>=requested_at 的用户真实输入(human.turn 证据)才接受留痕;Agent 转述无效。
|
|
20
|
-
- 用户修改 → 按修改意见修订产物,然后重新 --request。
|
|
21
|
-
- 用户驳回 → `flow_gate(reject=true, note=<原因>)` / `flow gate $1 $2 --reject --note <原因>`,进入修订。
|
|
22
|
-
|
|
23
|
-
硬性规则:
|
|
24
|
-
- 门控必须由 gate-verifier 判定,不得主观放行;不通过则列出缺失项并停止推进。
|
|
25
|
-
- 通过后必须用 `--record` 写入门控留痕 + 产物冻结 hash(gate.pass / gate.freeze 审计同一次原子写)。
|
|
26
|
-
- pending 门禁的 `--record` 必须 `--confirm <人类确认人>` 且有 HUMAN_TURN 证据;缺任一即 [REJECT]。
|
|
27
|
-
- 无 pending 请求的旧路径(直接 --record)仍可用但仅 WARN(兼容历史流程);新流程一律两步确认。
|
|
28
|
-
- 被门控产物事后任何变化 → validate 报 [GATE_INVALIDATED],门控失效,修改后须重新过门。
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: flow-transition
|
|
3
|
-
description: 状态迁移守卫(P0-1):合法迁移表 + 前置条件守卫 + 审计先行原子写
|
|
4
|
-
argument-hint: <实体ID> <目标状态> [--by <操作者>]
|
|
5
|
-
---
|
|
6
|
-
执行受守卫的状态迁移。参数:$1=实体ID(REQ-xxx / WU-xx / CHG-xxx / REL-xxx),$2=目标状态,$3=操作者(可选,默认 编排Agent)。
|
|
7
|
-
若已配置 flow MCP,调用 mcp__flow__flow_transition 工具;否则运行 `python .aidlc-engine/cli/flow.py transition $1 $2 --by <操作者>`。
|
|
8
|
-
|
|
9
|
-
硬性规则:
|
|
10
|
-
- 合法迁移表唯一真相源:`.aidlc-engine/state/state-machine.json`(REQ/WU/CHG/REL 四条流 + 状态-门控不变量)。
|
|
11
|
-
- 非法跳转或前置条件不满足 → [REJECT] + 类型化错误码(GUARD_ILLEGAL_JUMP / GUARD_GATE_MISSING / GUARD_WU_GATE_MISSING / GUARD_OBS_FAIL 等)+ 可执行的补救命令,拒绝并审计(status.change blocked)。
|
|
12
|
-
- 迁移成功时,status.change 审计与状态变更在同一次原子写中完成(审计先行不变量)。
|
|
13
|
-
- 禁止绕过本命令手改状态文件(硬约束路径,见 directory-convention.md)。
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: flow-validate
|
|
3
|
-
description: 校验状态机、门控冻结、人在场、审计规约、产物完整性与追溯链
|
|
4
|
-
---
|
|
5
|
-
校验 workflow-state.json:状态机流转合法性、门控冻结是否失效([INVALIDATED])、人在场门控([HUMAN_ABSENT])、
|
|
6
|
-
审计规约(seq 连续 / action 属注册表 / 审计先行)、每状态必需产物完整性、REQ↔EPIC↔WU↔CHG↔REL↔OBS 追溯链。
|
|
7
|
-
若已配置 flow MCP,调用 mcp__flow__flow_validate 工具;否则运行 `python .aidlc-engine/cli/flow.py validate`。
|
|
8
|
-
输出所有 [ERROR] / [WARN],存在 ERROR 时不得继续推进流程。
|