agentic-flow-kit 0.8.0 → 0.9.1
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/README.md +93 -69
- package/modules/gates/README.md +24 -0
- package/modules/gates/generated-readonly/README.md +13 -0
- package/modules/gates/generated-readonly/check-generated.mjs +61 -0
- package/modules/gates/generated-readonly/local-pre-commit.line +1 -0
- package/modules/gates/node-layer/README.md +9 -0
- package/modules/gates/node-layer/check-node-layer.mjs +29 -0
- package/modules/gates/node-layer/local-pre-commit.line +1 -0
- package/modules/gates/py-import/README.md +9 -0
- package/modules/gates/py-import/check-py-import.mjs +30 -0
- package/modules/gates/py-import/local-pre-commit.line +1 -0
- package/modules/hosts/claude/agents/implementer.md +52 -0
- package/modules/hosts/claude/agents/independent-reviewer.md +49 -0
- package/modules/hosts/claude/agents/ui-verifier.md +48 -0
- package/modules/hosts/claude/commands/wf-build.md +79 -0
- package/modules/hosts/claude/commands/wf-deploy.md +57 -0
- package/modules/hosts/claude/commands/wf-design.md +42 -0
- package/modules/hosts/claude/commands/wf-gate-checklist.md +77 -0
- package/modules/hosts/claude/commands/wf-maintain.md +76 -0
- package/modules/hosts/claude/commands/wf-new-task.md +65 -0
- package/modules/hosts/claude/commands/wf-plan.md +41 -0
- package/modules/hosts/claude/commands/wf-review.md +47 -0
- package/modules/hosts/claude/commands/wf-source-sync-check.md +89 -0
- package/modules/hosts/claude/commands/wf-sync-hosts.md +66 -0
- package/modules/hosts/claude/commands/wf-test.md +66 -0
- package/modules/hosts/codex/agents/implementer.md +52 -0
- package/modules/hosts/codex/agents/independent-reviewer.md +49 -0
- package/modules/hosts/codex/agents/ui-verifier.md +48 -0
- package/modules/hosts/codex/commands/wf-build.md +79 -0
- package/modules/hosts/codex/commands/wf-deploy.md +57 -0
- package/modules/hosts/codex/commands/wf-design.md +42 -0
- package/modules/hosts/codex/commands/wf-gate-checklist.md +77 -0
- package/modules/hosts/codex/commands/wf-maintain.md +76 -0
- package/modules/hosts/codex/commands/wf-new-task.md +65 -0
- package/modules/hosts/codex/commands/wf-plan.md +41 -0
- package/modules/hosts/codex/commands/wf-review.md +47 -0
- package/modules/hosts/codex/commands/wf-source-sync-check.md +89 -0
- package/modules/hosts/codex/commands/wf-sync-hosts.md +66 -0
- package/modules/hosts/codex/commands/wf-test.md +66 -0
- package/modules/hosts/codex/skills/flow-kit/SKILL.md +14 -0
- package/modules/hosts/cursor/agents/implementer.md +52 -0
- package/modules/hosts/cursor/agents/independent-reviewer.md +49 -0
- package/modules/hosts/cursor/agents/ui-verifier.md +48 -0
- package/modules/hosts/cursor/commands/wf-build.md +79 -0
- package/modules/hosts/cursor/commands/wf-deploy.md +57 -0
- package/modules/hosts/cursor/commands/wf-design.md +42 -0
- package/modules/hosts/cursor/commands/wf-gate-checklist.md +77 -0
- package/modules/hosts/cursor/commands/wf-maintain.md +76 -0
- package/modules/hosts/cursor/commands/wf-new-task.md +65 -0
- package/modules/hosts/cursor/commands/wf-plan.md +41 -0
- package/modules/hosts/cursor/commands/wf-review.md +47 -0
- package/modules/hosts/cursor/commands/wf-source-sync-check.md +89 -0
- package/modules/hosts/cursor/commands/wf-sync-hosts.md +66 -0
- package/modules/hosts/cursor/commands/wf-test.md +66 -0
- package/modules/hosts/omp/agents/implementer.md +1 -1
- package/modules/hosts/omp/agents/independent-reviewer.md +1 -1
- package/modules/hosts/omp/agents/ui-verifier.md +1 -1
- package/modules/hosts/opencode/agents/implementer.md +1 -1
- package/modules/hosts/opencode/agents/independent-reviewer.md +1 -1
- package/modules/hosts/opencode/agents/ui-verifier.md +1 -1
- package/modules/hosts/opencode/commands/wf-build.md +3 -4
- package/modules/hosts/opencode/commands/wf-design.md +2 -2
- package/modules/hosts/opencode/commands/wf-maintain.md +1 -1
- package/modules/hosts/opencode/commands/wf-plan.md +3 -3
- package/modules/hosts/opencode/commands/wf-review.md +1 -1
- package/modules/hosts/opencode/commands/wf-source-sync-check.md +1 -1
- package/modules/hosts/opencode/commands/wf-test.md +3 -1
- package/modules/hosts/trae/agents/implementer.md +1 -1
- package/modules/hosts/trae/agents/independent-reviewer.md +1 -1
- package/modules/hosts/trae/agents/ui-verifier.md +1 -1
- package/modules/hosts/trae/commands/wf-build.md +3 -4
- package/modules/hosts/trae/commands/wf-design.md +2 -2
- package/modules/hosts/trae/commands/wf-maintain.md +1 -1
- package/modules/hosts/trae/commands/wf-plan.md +3 -3
- package/modules/hosts/trae/commands/wf-review.md +1 -1
- package/modules/hosts/trae/commands/wf-source-sync-check.md +1 -1
- package/modules/hosts/trae/commands/wf-test.md +3 -1
- package/modules/hosts/zcode/agents/implementer.md +1 -1
- package/modules/hosts/zcode/agents/independent-reviewer.md +1 -1
- package/modules/hosts/zcode/agents/ui-verifier.md +1 -1
- package/package.json +45 -45
- package/src/cli.mjs +16 -4
- package/src/doctor.mjs +49 -1
- package/src/fresh-init.test.mjs +36 -0
- package/src/gates.test.mjs +126 -0
- package/src/init.mjs +8 -1
- package/src/init.test.mjs +7 -1
- package/src/profiles.mjs +10 -4
- package/src/run-tests.mjs +3 -0
- package/src/stack-profile.test.mjs +89 -0
- package/src/sync-hosts.test.mjs +26 -6
- package/templates/AGENTS.md +4 -4
- package/templates/_agents/board/index.html +3 -3
- package/templates/_agents/commands/build.md +4 -5
- package/templates/_agents/commands/design.md +4 -4
- package/templates/_agents/commands/maintain.md +1 -1
- package/templates/_agents/commands/new-task.md +13 -2
- package/templates/_agents/commands/plan.md +4 -4
- package/templates/_agents/commands/review.md +1 -1
- package/templates/_agents/commands/source-sync-check.md +1 -1
- package/templates/_agents/commands/test.md +4 -2
- package/templates/_agents/hooks/commit-check.cjs +45 -24
- package/templates/_agents/lane-surfaces.txt +14 -0
- package/templates/_agents/roles/implementer.md +1 -1
- package/templates/_agents/roles/independent-reviewer.md +1 -1
- package/templates/_agents/roles/ui-verifier.md +1 -1
- package/templates/_agents/scripts/check-lane-surface.mjs +164 -0
- package/templates/_agents/scripts/check-lane-surface.test.mjs +200 -0
- package/templates/_agents/scripts/check-loop-rev.test.mjs +337 -0
- package/templates/_agents/scripts/check-loop.mjs +460 -268
- package/templates/_agents/scripts/check-loop.test.mjs +677 -17
- package/templates/_agents/scripts/check-metric-claims.mjs +275 -0
- package/templates/_agents/scripts/commit-check-trigger.test.mjs +64 -1
- package/templates/_agents/scripts/confirm-doc.mjs +159 -15
- package/templates/_agents/scripts/confirm-doc.test.mjs +362 -10
- package/templates/_agents/scripts/doctor.test.mjs +102 -1
- package/templates/_agents/scripts/fill-intent.mjs +8 -7
- package/templates/_agents/scripts/fill-intent.test.mjs +12 -3
- package/templates/_agents/scripts/fill-plan.mjs +27 -11
- package/templates/_agents/scripts/fill-plan.test.mjs +148 -8
- package/templates/_agents/scripts/fill-spec.mjs +18 -3
- package/templates/_agents/scripts/fill-spec.test.mjs +198 -1
- package/templates/_agents/scripts/gate-checklist.mjs +4 -1
- package/templates/_agents/scripts/gen-workflow-index.mjs +6 -2
- package/templates/_agents/scripts/gen-workflow-index.test.mjs +12 -0
- package/templates/_agents/scripts/policy.mjs +46 -0
- package/templates/_agents/scripts/pre-push.test.mjs +82 -0
- package/templates/_agents/scripts/solidify-task.mjs +107 -0
- package/templates/_agents/scripts/solidify-task.test.mjs +126 -0
- package/templates/_agents/scripts/stage-gates.mjs +256 -0
- package/templates/_agents/scripts/trust-mode.mjs +67 -0
- package/templates/_agents/scripts/wf-journal.mjs +71 -2
- package/templates/_agents/scripts/wf-journal.test.mjs +66 -0
- package/templates/_agents/scripts/workflow-board-server.mjs +18 -15
- package/templates/_agents/scripts/workflow-board-server.test.mjs +30 -1
- package/templates/_agents/workflows/pipeline-closing.md +1 -1
- package/templates/_githooks/pre-commit +33 -2
- package/templates/_githooks/pre-push +58 -10
- package/templates/workflow/README.md +22 -3
- package/templates/workflow/incidents/_TEMPLATE.md +4 -0
- package/templates/workflow/intents/_TEMPLATE.md +1 -0
|
@@ -11,6 +11,8 @@ import path from 'node:path';
|
|
|
11
11
|
import { spawnSync } from 'node:child_process';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
13
|
import { computeFingerprint } from './confirm-doc.mjs';
|
|
14
|
+
import { POLICIES, loadKitPolicy } from './policy.mjs';
|
|
15
|
+
import { entryConfirmed, MARK_RE } from './stage-gates.mjs';
|
|
14
16
|
|
|
15
17
|
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
16
18
|
const CHECK_LOOP = path.join(SCRIPT_DIR, 'check-loop.mjs');
|
|
@@ -62,8 +64,9 @@ ${refs ? ' - 是 → ../intents/2026-09-12-ghost.md\n' : ' - 否 → 理由:
|
|
|
62
64
|
- AGENTS.md 某节
|
|
63
65
|
`;
|
|
64
66
|
|
|
65
|
-
// run:默认 CHECK_LOOP_ROOT 注入(fixture 模式);{git:true} 时 cwd 注入(真 git 模式,测 tracked
|
|
66
|
-
|
|
67
|
+
// run:默认 CHECK_LOOP_ROOT 注入(fixture 模式);{git:true} 时 cwd 注入(真 git 模式,测 tracked 过滤);
|
|
68
|
+
// opts.args 追加 CLI 参数(--hardening 等加固门场景用,2026-09-30 hybrid-governance-explore-hardening)
|
|
69
|
+
const run = (root, opts = {}) => spawnSync(process.execPath, [CHECK_LOOP, ...(opts.args || [])], {
|
|
67
70
|
...(opts.git ? { cwd: root } : { cwd: ROOT_CWD, env: { ...process.env, CHECK_LOOP_ROOT: root } }),
|
|
68
71
|
encoding: 'utf8',
|
|
69
72
|
});
|
|
@@ -547,8 +550,8 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
547
550
|
r.status === 0 && !outOf(r).includes('验收缺证据'), `exit=${r.status}\n${outOf(r)}`);
|
|
548
551
|
rmfix(T);
|
|
549
552
|
}
|
|
550
|
-
// ---- 场景 41:检查 8——[x] 无证据且续行也无 →
|
|
551
|
-
// 换锚后须真 git 仓:否则不可判定 → 走存量口径,「验收缺证据」这条
|
|
553
|
+
// ---- 场景 41:检查 8——[x] 无证据且续行也无 → 报验收缺证据(hard 拦;回归:不可把豁免放大成漏检)----
|
|
554
|
+
// 换锚后须真 git 仓:否则不可判定 → 走存量口径,「验收缺证据」这条 blocker 也不再产出。
|
|
552
555
|
{
|
|
553
556
|
const T = mkfix();
|
|
554
557
|
gitInit(T);
|
|
@@ -557,8 +560,8 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
557
560
|
w(T, 'workflow/plans/2026-09-12-ev2.md', PLAN('ev2', '状态: done\n级别: L1'));
|
|
558
561
|
gitCommitAll(T, 'add');
|
|
559
562
|
const r = run(T);
|
|
560
|
-
check('[x] 续行仍无证据 →
|
|
561
|
-
r.status ===
|
|
563
|
+
check('[x] 续行仍无证据 → 报验收缺证据(hard 拦 exit 1)',
|
|
564
|
+
r.status === 1 && outOf(r).includes('验收缺证据'), `exit=${r.status}\n${outOf(r)}`);
|
|
562
565
|
rmfix(T);
|
|
563
566
|
}
|
|
564
567
|
// ---- 场景 42:检查 4——fill-{intent,spec,plan}.mjs 花括号展开,三路皆存在 → 不报引用断档 ----
|
|
@@ -1107,12 +1110,19 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
1107
1110
|
{
|
|
1108
1111
|
const MC = 'ledger.lines = ledger.lines\ndocs.count.plans = docs.count.plans\n';
|
|
1109
1112
|
const seedLedger = (T, lines) => w(T, '.agents/confirmations.jsonl', lines.join('\n') + '\n');
|
|
1113
|
+
// T2 口径适配(2026-09-28 check16-inline-debt):scanFiles16 统一走 docFiles(.md ∧ 数字前缀 ∧ tracked),
|
|
1114
|
+
// 非数字前缀件不再进检查 16 扫描面——原 p1/p2/… 无前缀文件名曾靠「旧扫描面收非前缀件」成立。改为
|
|
1115
|
+
// 数字前缀 + 同名 intent(沿场景 80-90 activePlan 先例:缺 intent 会被「配对断裂」hard-block 干扰断言)。
|
|
1116
|
+
const activePlan16 = (T, name, body) => {
|
|
1117
|
+
w(T, `workflow/intents/2026-09-01-${name}.md`, INTENT(name, '状态: draft\n级别: L1'));
|
|
1118
|
+
w(T, `workflow/plans/2026-09-01-${name}.md`, PLAN(name, '状态: draft\n级别: L1', body));
|
|
1119
|
+
};
|
|
1110
1120
|
{
|
|
1111
1121
|
// 【核心回归】活跃文档留签名未回填 → warning 且提示实时值
|
|
1112
1122
|
const T = mkfix();
|
|
1113
1123
|
w(T, '.agents/metric-claims.txt', MC);
|
|
1114
1124
|
seedLedger(T, ['{"doc":"a","stage":"done"}', '{"doc":"b","stage":"done"}', '{"doc":"c","stage":"done"}']);
|
|
1115
|
-
|
|
1125
|
+
activePlan16(T, 'p1', '\n台账共 {{ledger.lines}} 行\n');
|
|
1116
1126
|
const r = run(T);
|
|
1117
1127
|
const o = outOf(r);
|
|
1118
1128
|
check('检查16:活跃文档留签名未回填 → WARN 且给出实时值 3(advisory 不阻断)',
|
|
@@ -1124,7 +1134,7 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
1124
1134
|
const T = mkfix();
|
|
1125
1135
|
w(T, '.agents/metric-claims.txt', MC);
|
|
1126
1136
|
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1127
|
-
|
|
1137
|
+
activePlan16(T, 'p2', '\n台账共 47 行,其中 13 条并录(历史叙述,非签名)\n');
|
|
1128
1138
|
const r = run(T);
|
|
1129
1139
|
check('检查16:无签名 → 零告警(裸数字是历史叙述,不得产生假阳性)',
|
|
1130
1140
|
r.status === 0 && !outOf(r).includes('指标'), `exit=${r.status}\n${outOf(r)}`);
|
|
@@ -1140,8 +1150,8 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
1140
1150
|
const T = mkfix();
|
|
1141
1151
|
w(T, '.agents/metric-claims.txt', MC);
|
|
1142
1152
|
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1143
|
-
|
|
1144
|
-
'\n构建 = ' + SCREAMING('BUILD_CMD') + ';端口 ' + SCREAMING('BOARD_PORT') + ';项目 ' + SCREAMING('PROJECT_NAME') + '\n')
|
|
1153
|
+
activePlan16(T, 'p3',
|
|
1154
|
+
'\n构建 = ' + SCREAMING('BUILD_CMD') + ';端口 ' + SCREAMING('BOARD_PORT') + ';项目 ' + SCREAMING('PROJECT_NAME') + '\n');
|
|
1145
1155
|
const r = run(T);
|
|
1146
1156
|
check('检查16:装户模板占位符(全大写 SCREAMING_CASE)→ 不误报(形态收窄)',
|
|
1147
1157
|
r.status === 0 && !outOf(r).includes('指标'), `exit=${r.status}\n${outOf(r)}`);
|
|
@@ -1152,7 +1162,7 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
1152
1162
|
const T = mkfix();
|
|
1153
1163
|
w(T, '.agents/metric-claims.txt', MC);
|
|
1154
1164
|
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1155
|
-
|
|
1165
|
+
activePlan16(T, 'p4', '\n引用 {{nope.metric}}\n');
|
|
1156
1166
|
const r = run(T);
|
|
1157
1167
|
check('检查16:引用未登记指标 → fail-loud 出账(不静默跳过)',
|
|
1158
1168
|
outOf(r).includes('指标未登记'), `exit=${r.status}\n${outOf(r)}`);
|
|
@@ -1164,8 +1174,7 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
1164
1174
|
const T = mkfix();
|
|
1165
1175
|
w(T, '.agents/metric-claims.txt', MC);
|
|
1166
1176
|
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1167
|
-
|
|
1168
|
-
'\n写法示例如 `\\{{ledger.lines}}`(本行是讲语法,非断言)\n'));
|
|
1177
|
+
activePlan16(T, 'p8', '\n写法示例如 `\\{{ledger.lines}}`(本行是讲语法,非断言)\n');
|
|
1169
1178
|
const r = run(T);
|
|
1170
1179
|
check('检查16:转义 \\{{...}} 的行为示意 → 不报(区分「讲语法」与「真断言」)',
|
|
1171
1180
|
r.status === 0 && !outOf(r).includes('指标'), `exit=${r.status}\n${outOf(r)}`);
|
|
@@ -1205,7 +1214,9 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
1205
1214
|
const T = mkfix();
|
|
1206
1215
|
w(T, '.agents/metric-claims.txt', MC);
|
|
1207
1216
|
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1208
|
-
|
|
1217
|
+
// 终态件(plan done + 同名 intent,数字前缀使其确在 docFiles 扫描面内——断言才针对「终态豁免」而非「无前缀豁免」)
|
|
1218
|
+
w(T, 'workflow/intents/2026-09-01-p7.md', INTENT('p7', '状态: draft\n级别: L1'));
|
|
1219
|
+
w(T, 'workflow/plans/2026-09-01-p7.md', PLAN('p7', '状态: done\n级别: L1', '\n台账共 {{ledger.lines}} 行\n'));
|
|
1209
1220
|
const r = run(T);
|
|
1210
1221
|
check('检查16:终态文档不扫(历史叙述豁免,沿检查 4 尺度)',
|
|
1211
1222
|
r.status === 0 && !outOf(r).includes('指标待回填'), `exit=${r.status}\n${outOf(r)}`);
|
|
@@ -1234,8 +1245,7 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
1234
1245
|
const T = mkfix();
|
|
1235
1246
|
w(T, '.agents/metric-claims.txt', MC);
|
|
1236
1247
|
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1237
|
-
|
|
1238
|
-
'\n讲语法 \\{{a.b}} 示意,但本行也有真断言 {{ledger.lines}} 未回填\n'));
|
|
1248
|
+
activePlan16(T, 'p9', '\n讲语法 \\{{a.b}} 示意,但本行也有真断言 {{ledger.lines}} 未回填\n');
|
|
1239
1249
|
const r = run(T);
|
|
1240
1250
|
check('检查16:同行「转义示意 + 真未回填」→ 真签名仍报(转义按逐个出现,不按整行)',
|
|
1241
1251
|
outOf(r).includes('指标待回填'), `exit=${r.status}\n${outOf(r)}`);
|
|
@@ -1246,7 +1256,7 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
1246
1256
|
const T = mkfix();
|
|
1247
1257
|
w(T, '.agents/metric-claims.txt', MC);
|
|
1248
1258
|
seedLedger(T, ['{"doc":"a","stage":"done"}']);
|
|
1249
|
-
|
|
1259
|
+
activePlan16(T, 'p10', '\n引用 {{Ledger.Lines}} 笔误\n');
|
|
1250
1260
|
const r = run(T);
|
|
1251
1261
|
check('检查16:形态不符(大写点分笔误)→ 出账「指标形态」(不静默漏过)',
|
|
1252
1262
|
outOf(r).includes('指标形态'), `exit=${r.status}\n${outOf(r)}`);
|
|
@@ -1593,5 +1603,655 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
1593
1603
|
}
|
|
1594
1604
|
}
|
|
1595
1605
|
|
|
1606
|
+
// ---- 场景 91:检查 16 tracked-only 口径双向(2026-09-28 check16-inline-debt T2)----
|
|
1607
|
+
// scanFiles16 与取数器统一走 docFiles(.md ∧ 数字前缀 ∧ tracked):并行会话的未跟踪半成品即便含
|
|
1608
|
+
// 未回填签名也不进扫描面(别人的草稿不拦我的 push,沿检查 1-15 仓库模式「只扫已提交(HEAD)」口径);
|
|
1609
|
+
// 提交进 HEAD 后照常出账。跑法沿场景 23/24 先例({git:true} + cwd 注入真 git 模式)。
|
|
1610
|
+
{
|
|
1611
|
+
const T = mkfix();
|
|
1612
|
+
gitInit(T);
|
|
1613
|
+
w(T, '.agents/metric-claims.txt', 'ledger.lines = ledger.lines\n');
|
|
1614
|
+
w(T, '.agents/confirmations.jsonl', '{"doc":"a","stage":"done"}\n');
|
|
1615
|
+
w(T, 'workflow/intents/2026-09-12-base16.md', INTENT('base16', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
1616
|
+
w(T, 'workflow/plans/2026-09-12-base16.md', PLAN('base16', '状态: done\n级别: L1'));
|
|
1617
|
+
gitCommitAll(T, 'base');
|
|
1618
|
+
// 未跟踪半成品:留签名未回填 → 不出账(tracked-only:不进扫描面)
|
|
1619
|
+
w(T, 'workflow/intents/2026-09-28-wip16.md', INTENT('wip16', '状态: draft\n级别: L1\n日期: 2026-09-28'));
|
|
1620
|
+
w(T, 'workflow/plans/2026-09-28-wip16.md', PLAN('wip16', '状态: draft\n级别: L1', '\n台账共 {{ledger.lines}} 行\n'));
|
|
1621
|
+
// 根级 workflow/*.md 同口径钉住(2026-09-29 复核 P2-1:根级 tracked 过滤曾无测试承重——变异去除后全绿)
|
|
1622
|
+
w(T, 'workflow/root-wip16.md', '---\n状态: draft\n级别: L1\n日期: 2026-09-28\n---\n# ROOT-WIP16\n\n根级草稿台账 {{ledger.lines}} 行\n');
|
|
1623
|
+
let r = run(T, { git: true });
|
|
1624
|
+
check('检查16 tracked-only:未跟踪档含未回填签名 → 不出账(并行半成品不进扫描面;含根级)',
|
|
1625
|
+
r.status === 0 && !outOf(r).includes('指标待回填') && !outOf(r).includes('wip16') && !outOf(r).includes('root-wip16'),
|
|
1626
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1627
|
+
gitCommitAll(T, 'wip');
|
|
1628
|
+
r = run(T, { git: true });
|
|
1629
|
+
check('检查16 tracked-only:提交进 HEAD 后 → 照常出账(WARN 指标待回填;含根级)',
|
|
1630
|
+
r.status === 0 && outOf(r).includes('指标待回填') && outOf(r).includes('wip16') && outOf(r).includes('root-wip16'),
|
|
1631
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1632
|
+
rmfix(T);
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
// ---- audit 档:false 只留阻断;缺省仍出卫生警告 ----
|
|
1636
|
+
{
|
|
1637
|
+
const T = mkfix();
|
|
1638
|
+
w(T, '.agents/kit.json', '{"audit":false,"policyVersion":1}\n');
|
|
1639
|
+
w(T, 'workflow/intents/2026-09-12-ph.md', INTENT('ph', '状态: draft\n级别: L1\n日期: YYYY-MM-DD'));
|
|
1640
|
+
w(T, 'workflow/plans/2026-09-12-ph.md', PLAN('ph', '状态: draft\n级别: L1'));
|
|
1641
|
+
const r = run(T);
|
|
1642
|
+
check('audit false:占位符不出账且 exit 0', r.status === 0 && !outOf(r).includes('模板未填'), `exit=${r.status}\n${outOf(r)}`);
|
|
1643
|
+
rmfix(T);
|
|
1644
|
+
}
|
|
1645
|
+
{
|
|
1646
|
+
const T = mkfix();
|
|
1647
|
+
w(T, '.agents/kit.json', '{"audit":false,"policyVersion":1}\n');
|
|
1648
|
+
w(T, 'workflow/intents/2026-09-12-np.md', INTENT('np', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1649
|
+
expectHard('audit false:缺 plan 仍阻断', T, '配对断裂');
|
|
1650
|
+
rmfix(T);
|
|
1651
|
+
}
|
|
1652
|
+
{
|
|
1653
|
+
const T = mkfix();
|
|
1654
|
+
w(T, 'workflow/intents/2026-09-12-ph2.md', INTENT('ph2', '状态: draft\n级别: L1\n日期: YYYY-MM-DD'));
|
|
1655
|
+
w(T, 'workflow/plans/2026-09-12-ph2.md', PLAN('ph2', '状态: draft\n级别: L1'));
|
|
1656
|
+
const r = run(T);
|
|
1657
|
+
check('audit 缺省:占位符仍警告', r.status === 0 && outOf(r).includes('模板未填'), `exit=${r.status}\n${outOf(r)}`);
|
|
1658
|
+
rmfix(T);
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// ---- 检查 17:发版提交树上的 draft 仍 draft 才阻断;发版之后新建的不拦 ----
|
|
1662
|
+
{
|
|
1663
|
+
const T = mkfix();
|
|
1664
|
+
gitInit(T);
|
|
1665
|
+
w(T, 'package.json', '{"name":"t","version":"0.1.0"}\n');
|
|
1666
|
+
w(T, 'workflow/intents/2026-09-12-old.md', INTENT('old', '状态: draft\n级别: L1\n日期: 2026-09-12'));
|
|
1667
|
+
w(T, 'workflow/plans/2026-09-12-old.md', PLAN('old', '状态: draft\n级别: L1'));
|
|
1668
|
+
gitCommitAll(T, 'v0.1.0');
|
|
1669
|
+
let r = run(T, { git: true });
|
|
1670
|
+
check('检查17 发版树上的 draft 仍为 draft → hard', r.status === 1 && outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1671
|
+
w(T, 'workflow/intents/2026-09-12-old.md', INTENT('old', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1672
|
+
w(T, 'workflow/plans/2026-09-12-old.md', PLAN('old', '状态: approved\n级别: L1'));
|
|
1673
|
+
w(T, 'package.json', '{"name":"t","version":"0.2.0"}\n');
|
|
1674
|
+
gitCommitAll(T, 'v0.2.0');
|
|
1675
|
+
w(T, 'workflow/intents/2026-09-29-after.md', INTENT('after', '状态: draft\n级别: L1\n日期: 2026-09-29'));
|
|
1676
|
+
w(T, 'workflow/plans/2026-09-29-after.md', PLAN('after', '状态: draft\n级别: L1'));
|
|
1677
|
+
gitCommitAll(T, 'draft after release');
|
|
1678
|
+
r = run(T, { git: true });
|
|
1679
|
+
check('检查17 发版之后新建的 draft 不拦', r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1680
|
+
rmfix(T);
|
|
1681
|
+
}
|
|
1682
|
+
{
|
|
1683
|
+
const T = mkfix();
|
|
1684
|
+
gitInit(T);
|
|
1685
|
+
w(T, 'package.json', '{"name":"t","version":"0.1.0"}\n');
|
|
1686
|
+
w(T, 'workflow/intents/2026-09-12-deep.md', INTENT('deep', '状态: draft\n级别: L1\n日期: 2026-09-12'));
|
|
1687
|
+
w(T, 'workflow/plans/2026-09-12-deep.md', PLAN('deep', '状态: draft\n级别: L1'));
|
|
1688
|
+
gitCommitAll(T, 'v0.1.0');
|
|
1689
|
+
for (let i = 1; i <= 41; i++) {
|
|
1690
|
+
w(T, 'package.json', `{"name":"t","version":"0.1.0","n":${i}}\n`);
|
|
1691
|
+
gitCommitAll(T, `touch ${i}`);
|
|
1692
|
+
}
|
|
1693
|
+
const r = run(T, { git: true });
|
|
1694
|
+
check('检查17 版本变更早于 40 次 package.json 触碰仍阻断', r.status === 1 && outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1695
|
+
rmfix(T);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
// ---- 检查 17:approved 锚在 policy 版本 2;draft 改为 approved 不看锚 ----
|
|
1699
|
+
{
|
|
1700
|
+
const sameKeys = ['moduleSince', 'confirmDocsEffective', 'confirmIncidentsEffective', 'bindingTs'];
|
|
1701
|
+
check('policy 版本 2 的四个共享日期与版本 1 相同',
|
|
1702
|
+
sameKeys.every((k) => POLICIES[1][k] === POLICIES[2][k]));
|
|
1703
|
+
check('policy 版本 2 check14Since 后移至 2026-09-26(确认门实际上线日)',
|
|
1704
|
+
POLICIES[2].check14Since === '2026-09-26' && POLICIES[1].check14Since === '2026-09-23');
|
|
1705
|
+
check('policy 版本 2 新增 check17UnclosedAfter,版本 1 无此键',
|
|
1706
|
+
POLICIES[2].check17UnclosedAfter === '0.8.0' && POLICIES[1].check17UnclosedAfter === undefined);
|
|
1707
|
+
const T = mkfix();
|
|
1708
|
+
w(T, '.agents/kit.json', '{"policyVersion":99}\n');
|
|
1709
|
+
const p = loadKitPolicy(T);
|
|
1710
|
+
check('未知 policyVersion 回退版本 1', p.policyVersion === 1 && p.check17UnclosedAfter === undefined);
|
|
1711
|
+
rmfix(T);
|
|
1712
|
+
}
|
|
1713
|
+
{
|
|
1714
|
+
const T = mkfix();
|
|
1715
|
+
gitInit(T);
|
|
1716
|
+
w(T, 'package.json', '{"name":"t","version":"0.1.0"}\n');
|
|
1717
|
+
w(T, 'workflow/intents/2026-09-12-move.md', INTENT('move', '状态: draft\n级别: L1\n日期: 2026-09-12'));
|
|
1718
|
+
w(T, 'workflow/plans/2026-09-12-move.md', PLAN('move', '状态: draft\n级别: L1'));
|
|
1719
|
+
gitCommitAll(T, 'v0.1.0');
|
|
1720
|
+
w(T, 'workflow/intents/2026-09-12-move.md', INTENT('move', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1721
|
+
w(T, 'workflow/plans/2026-09-12-move.md', PLAN('move', '状态: approved\n级别: L1'));
|
|
1722
|
+
gitCommitAll(T, 'leave draft');
|
|
1723
|
+
const r = run(T, { git: true });
|
|
1724
|
+
check('检查17 发版树上是 draft、现在是 approved → hard',
|
|
1725
|
+
r.status === 1 && outOf(r).includes('发版草稿') && outOf(r).includes('已是 draft') && outOf(r).includes('仍是 approved'),
|
|
1726
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1727
|
+
rmfix(T);
|
|
1728
|
+
}
|
|
1729
|
+
{
|
|
1730
|
+
const doneBody = '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n';
|
|
1731
|
+
const T = mkfix();
|
|
1732
|
+
gitInit(T);
|
|
1733
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1734
|
+
w(T, 'package.json', '{"name":"t","version":"0.8.0"}\n');
|
|
1735
|
+
gitCommitAll(T, 'v0.8.0');
|
|
1736
|
+
w(T, 'package.json', '{"name":"t","version":"0.9.0"}\n');
|
|
1737
|
+
w(T, 'workflow/intents/2026-09-12-stay.md', INTENT('stay', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1738
|
+
w(T, 'workflow/plans/2026-09-12-stay.md', PLAN('stay', '状态: approved\n级别: L1'));
|
|
1739
|
+
gitCommitAll(T, 'v0.9.0');
|
|
1740
|
+
let r = run(T, { git: true });
|
|
1741
|
+
check('检查17 锚之后 approved 仍是 approved → hard',
|
|
1742
|
+
r.status === 1 && outOf(r).includes('发版草稿') && outOf(r).includes('version=0.9.0'),
|
|
1743
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1744
|
+
w(T, 'workflow/intents/2026-09-12-stay.md', INTENT('stay', '状态: done\n级别: L1\n日期: 2026-09-12', doneBody));
|
|
1745
|
+
w(T, 'workflow/plans/2026-09-12-stay.md', PLAN('stay', '状态: done\n级别: L1'));
|
|
1746
|
+
gitCommitAll(T, 'close');
|
|
1747
|
+
r = run(T, { git: true });
|
|
1748
|
+
check('检查17 同一文件已是 done → 不拦', r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1749
|
+
rmfix(T);
|
|
1750
|
+
}
|
|
1751
|
+
{
|
|
1752
|
+
const T = mkfix();
|
|
1753
|
+
gitInit(T);
|
|
1754
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1755
|
+
w(T, 'package.json', '{"name":"t","version":"0.8.0"}\n');
|
|
1756
|
+
w(T, 'workflow/intents/2026-09-12-oldrel.md', INTENT('oldrel', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1757
|
+
w(T, 'workflow/plans/2026-09-12-oldrel.md', PLAN('oldrel', '状态: approved\n级别: L1'));
|
|
1758
|
+
gitCommitAll(T, 'v0.8.0');
|
|
1759
|
+
const r = run(T, { git: true });
|
|
1760
|
+
check('检查17 锚之前 approved 仍是 approved → 不拦', r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1761
|
+
rmfix(T);
|
|
1762
|
+
}
|
|
1763
|
+
{
|
|
1764
|
+
const T = mkfix();
|
|
1765
|
+
gitInit(T);
|
|
1766
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1767
|
+
w(T, 'package.json', '{"name":"t","version":"0.10.0"}\n');
|
|
1768
|
+
w(T, 'workflow/intents/2026-09-12-ten.md', INTENT('ten', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1769
|
+
w(T, 'workflow/plans/2026-09-12-ten.md', PLAN('ten', '状态: approved\n级别: L1'));
|
|
1770
|
+
gitCommitAll(T, 'v0.10.0');
|
|
1771
|
+
const r = run(T, { git: true });
|
|
1772
|
+
check('检查17 0.10.0 按整数比较大于 0.8.0 → hard',
|
|
1773
|
+
r.status === 1 && outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1774
|
+
rmfix(T);
|
|
1775
|
+
}
|
|
1776
|
+
{
|
|
1777
|
+
const T = mkfix();
|
|
1778
|
+
gitInit(T);
|
|
1779
|
+
w(T, '.agents/kit.json', '{"policyVersion":1}\n');
|
|
1780
|
+
w(T, 'package.json', '{"name":"t","version":"0.9.0"}\n');
|
|
1781
|
+
w(T, 'workflow/intents/2026-09-12-v1.md', INTENT('v1', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1782
|
+
w(T, 'workflow/plans/2026-09-12-v1.md', PLAN('v1', '状态: approved\n级别: L1'));
|
|
1783
|
+
gitCommitAll(T, 'v0.9.0');
|
|
1784
|
+
const r = run(T, { git: true });
|
|
1785
|
+
check('检查17 版本 1 不启用 approved 锚', r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1786
|
+
rmfix(T);
|
|
1787
|
+
}
|
|
1788
|
+
{
|
|
1789
|
+
const T = mkfix();
|
|
1790
|
+
gitInit(T);
|
|
1791
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1792
|
+
w(T, 'package.json', '{"name":"t","version":"0.9"}\n');
|
|
1793
|
+
w(T, 'workflow/intents/2026-09-12-badver.md', INTENT('badver', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1794
|
+
w(T, 'workflow/plans/2026-09-12-badver.md', PLAN('badver', '状态: approved\n级别: L1'));
|
|
1795
|
+
gitCommitAll(T, 'badver');
|
|
1796
|
+
const r = run(T, { git: true });
|
|
1797
|
+
check('检查17 发版版本拆不成三段整数时不触发 approved 规则',
|
|
1798
|
+
r.status === 0 && !outOf(r).includes('发版草稿'), `exit=${r.status}\n${outOf(r)}`);
|
|
1799
|
+
rmfix(T);
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// ---- 检查 18:委派台账对账 [warning],退出码仍为 0 ----
|
|
1803
|
+
{
|
|
1804
|
+
const T = mkfix();
|
|
1805
|
+
const gap = '2026-09-12-gap.md';
|
|
1806
|
+
w(T, `workflow/intents/${gap}`, INTENT('gap', '状态: done\n级别: L2\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
1807
|
+
w(T, `workflow/specs/${gap}`, SPEC('gap', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
1808
|
+
w(T, `workflow/plans/${gap}`, PLAN('gap', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
1809
|
+
w(T, 'workflow/intents/2026-09-12-low.md', INTENT('low', '状态: done\n级别: L1\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
1810
|
+
w(T, 'workflow/plans/2026-09-12-low.md', PLAN('low', '状态: done\n级别: L1\n日期: 2026-09-12'));
|
|
1811
|
+
w(T, 'workflow/intents/2026-09-12-stay.md', INTENT('stay', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
1812
|
+
w(T, 'workflow/specs/2026-09-12-stay.md', SPEC('stay', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
1813
|
+
w(T, 'workflow/plans/2026-09-12-stay.md', PLAN('stay', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
1814
|
+
const ledgerOf = (r) => outOf(r).split('\n').filter((l) => l.includes('委派台账'));
|
|
1815
|
+
let r = run(T);
|
|
1816
|
+
let lines = ledgerOf(r);
|
|
1817
|
+
check('检查18 委派表不存在:L2 done 警告且 exit 0;L1 done 与 L2 approved 不警告',
|
|
1818
|
+
r.status === 0
|
|
1819
|
+
&& lines.some((l) => l.includes(`intents/${gap}`) && l.includes('2026-09-12'))
|
|
1820
|
+
&& lines.every((l) => !l.includes('low.md') && !l.includes('stay.md')),
|
|
1821
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1822
|
+
|
|
1823
|
+
const head = '## 委派结果\n\n| 日期 | 被委派方 | 任务一句话 | 结果 | 备注 |\n|------|----------|------------|------|------|\n';
|
|
1824
|
+
w(T, 'workflow/delegations.md', `${head}| 2026-09-11 | x | y | 一次通过 | ${gap} |\n`);
|
|
1825
|
+
r = run(T);
|
|
1826
|
+
check('检查18 行日期早于文档日期:警告仍在',
|
|
1827
|
+
r.status === 0 && ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
1828
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1829
|
+
|
|
1830
|
+
w(T, 'workflow/delegations.md', `${head}| 2026-09-12 | x | y | 一次通过 | 2026-09-12-gap |\n`);
|
|
1831
|
+
r = run(T);
|
|
1832
|
+
check('检查18 去掉扩展名的主题名也命中:警告消失',
|
|
1833
|
+
r.status === 0 && !ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
1834
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1835
|
+
|
|
1836
|
+
w(T, 'workflow/delegations.md', `${head}\n## 自做任务结果\n\n| 日期 | 任务一句话 | 结果 | 备注 |\n|------|------------|------|------|\n| 2026-09-12 | x | 一次通过 | ${gap} |\n`);
|
|
1837
|
+
r = run(T);
|
|
1838
|
+
check('检查18 文件名在自做任务结果表:警告消失',
|
|
1839
|
+
r.status === 0 && !ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
1840
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1841
|
+
|
|
1842
|
+
// 补缺(2026-09-30 F2):自做表行**不带 .md**(台账自做表实际通写形态)——此前矩阵只覆盖三格:
|
|
1843
|
+
// 委派表带/不带 .md、自做表带 .md 均有例,唯独「自做表 + 不带 .md」从未穿越(夹具 gap 恒带 .md,
|
|
1844
|
+
// 走的是 includes(base) 分支)。本用例专走 includes(stripped) 分支,矩阵补成 4/4。
|
|
1845
|
+
w(T, 'workflow/delegations.md', `${head}\n## 自做任务结果\n\n| 日期 | 任务一句话 | 结果 | 备注 |\n|------|------------|------|------|\n| 2026-09-12 | x | 一次通过 | 2026-09-12-gap |\n`);
|
|
1846
|
+
r = run(T);
|
|
1847
|
+
check('检查18 自做表行不带 .md(台账实际通写形态):警告消失',
|
|
1848
|
+
r.status === 0 && !ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
1849
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1850
|
+
|
|
1851
|
+
w(T, 'workflow/delegations.md', `${head}| 2026-09-12 | x | y | 一次通过 | ${gap} |\n`);
|
|
1852
|
+
r = run(T);
|
|
1853
|
+
lines = ledgerOf(r);
|
|
1854
|
+
check('检查18 补上不早于文档日期且含文件名的一行:该警告消失',
|
|
1855
|
+
r.status === 0 && !lines.some((l) => l.includes(gap)),
|
|
1856
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1857
|
+
|
|
1858
|
+
// 文件名仅出现在非台账节(既非委派结果也非自做任务结果):不命中
|
|
1859
|
+
w(T, 'workflow/delegations.md', `${head}| 2026-09-12 | x | y | 一次通过 | 其他任务 |\n\n## 历史记录\n| ${gap} 已关单 |\n`);
|
|
1860
|
+
r = run(T);
|
|
1861
|
+
check('检查18 文件名仅出现在非台账节:警告仍在',
|
|
1862
|
+
r.status === 0 && ledgerOf(r).some((l) => l.includes(`intents/${gap}`)),
|
|
1863
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1864
|
+
rmfix(T);
|
|
1865
|
+
}
|
|
1866
|
+
{
|
|
1867
|
+
const T = mkfix();
|
|
1868
|
+
const body = 三件套(false);
|
|
1869
|
+
w(T, 'workflow/incidents/2026-09-12-closed.md', `---\n状态: closed\n级别: L2\n发现: 2026-09-12\n---\n# INCIDENT — closed\n\n${body}`);
|
|
1870
|
+
w(T, 'workflow/specs/2026-09-12-closed.md', SPEC('closed', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
1871
|
+
w(T, 'workflow/plans/2026-09-12-closed.md', PLAN('closed', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
1872
|
+
w(T, 'workflow/incidents/2026-09-12-l1c.md', `---\n状态: closed\n级别: L1\n发现: 2026-09-12\n---\n# INCIDENT — l1c\n\n${body}`);
|
|
1873
|
+
w(T, 'workflow/plans/2026-09-12-l1c.md', PLAN('l1c', '状态: approved\n级别: L1\n日期: 2026-09-12'));
|
|
1874
|
+
w(T, 'workflow/incidents/2026-09-12-open.md', `---\n状态: open\n级别: L2\n发现: 2026-09-12\n---\n# INCIDENT — open\n\n${body}`);
|
|
1875
|
+
w(T, 'workflow/specs/2026-09-12-open.md', SPEC('open', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
1876
|
+
w(T, 'workflow/plans/2026-09-12-open.md', PLAN('open', '状态: approved\n级别: L2\n日期: 2026-09-12'));
|
|
1877
|
+
const r = run(T);
|
|
1878
|
+
const lines = outOf(r).split('\n').filter((l) => l.includes('委派台账'));
|
|
1879
|
+
check('检查18 L2 closed 出警告且 exit 0;L1 closed 与 L2 open 不出',
|
|
1880
|
+
r.status === 0
|
|
1881
|
+
&& lines.some((l) => l.includes('incidents/2026-09-12-closed.md'))
|
|
1882
|
+
&& lines.every((l) => !l.includes('l1c.md') && !l.includes('open.md')),
|
|
1883
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1884
|
+
rmfix(T);
|
|
1885
|
+
}
|
|
1886
|
+
{
|
|
1887
|
+
const T = mkfix();
|
|
1888
|
+
w(T, 'workflow/intents/2026-09-12-nodate.md', INTENT('nodate', '状态: done\n级别: L2'));
|
|
1889
|
+
w(T, 'workflow/specs/2026-09-12-nodate.md', SPEC('nodate', '状态: approved\n级别: L2'));
|
|
1890
|
+
w(T, 'workflow/plans/2026-09-12-nodate.md', PLAN('nodate', '状态: approved\n级别: L2'));
|
|
1891
|
+
const r = run(T);
|
|
1892
|
+
check('检查18 范围内但缺日期:不警告',
|
|
1893
|
+
r.status === 0 && !outOf(r).includes('委派台账'),
|
|
1894
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1895
|
+
rmfix(T);
|
|
1896
|
+
}
|
|
1897
|
+
{
|
|
1898
|
+
const T = mkfix();
|
|
1899
|
+
w(T, '.agents/kit.json', '{"audit":false,"policyVersion":1}\n');
|
|
1900
|
+
w(T, 'workflow/intents/2026-09-12-quiet.md', INTENT('quiet', '状态: done\n级别: L2\n日期: 2026-09-12', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
1901
|
+
w(T, 'workflow/specs/2026-09-12-quiet.md', SPEC('quiet', '状态: done\n级别: L2\n日期: 2026-09-12'));
|
|
1902
|
+
w(T, 'workflow/plans/2026-09-12-quiet.md', PLAN('quiet', '状态: done\n级别: L2\n日期: 2026-09-12'));
|
|
1903
|
+
const r = run(T);
|
|
1904
|
+
check('检查18 audit false:委派台账不输出且 exit 0',
|
|
1905
|
+
r.status === 0 && !outOf(r).includes('委派台账'),
|
|
1906
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1907
|
+
rmfix(T);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
// ---- 检查 19:逐阶段审计 [warning](2026-09-30 stage-gate-machine)----
|
|
1911
|
+
{
|
|
1912
|
+
// 判据 A:spec/plan 为 draft 且日期 ≥ stageGateSince,入口未确认 → 告警(spec 与 plan 各一条)
|
|
1913
|
+
const T = mkfix();
|
|
1914
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1915
|
+
w(T, 'workflow/intents/2026-09-30-pair.md', INTENT('pair', '状态: draft\n级别: L2\n日期: 2026-09-30\n模块: pipeline'));
|
|
1916
|
+
w(T, 'workflow/specs/2026-09-30-pair.md', SPEC('pair', '状态: draft\n级别: L2\n日期: 2026-09-30\n模块: pipeline'));
|
|
1917
|
+
w(T, 'workflow/plans/2026-09-30-pair.md', PLAN('pair', '状态: draft\n级别: L2\n模块: pipeline'));
|
|
1918
|
+
const r = run(T);
|
|
1919
|
+
const lines = outOf(r).split('\n').filter((l) => l.includes('逐阶段'));
|
|
1920
|
+
check('检查19-A 入口未确认:spec 出「起草先于入口确认」、plan 同步告警,exit 0',
|
|
1921
|
+
r.status === 0
|
|
1922
|
+
&& lines.some((l) => l.includes('specs/2026-09-30-pair.md') && l.includes('起草先于入口确认'))
|
|
1923
|
+
&& lines.some((l) => l.includes('plans/2026-09-30-pair.md')),
|
|
1924
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1925
|
+
rmfix(T);
|
|
1926
|
+
}
|
|
1927
|
+
{
|
|
1928
|
+
// 判据 A 日期门:draft 日期早于 stageGateSince → 静默(历史豁免)
|
|
1929
|
+
const T = mkfix();
|
|
1930
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1931
|
+
w(T, 'workflow/intents/2026-09-20-old.md', INTENT('old', '状态: draft\n级别: L1\n日期: 2026-09-20'));
|
|
1932
|
+
w(T, 'workflow/plans/2026-09-20-old.md', PLAN('old', '状态: draft\n级别: L1'));
|
|
1933
|
+
const r = run(T);
|
|
1934
|
+
check('检查19-A 日期门:早于 stageGateSince 的 draft → 零逐阶段告警',
|
|
1935
|
+
r.status === 0 && !outOf(r).includes('逐阶段'),
|
|
1936
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1937
|
+
rmfix(T);
|
|
1938
|
+
}
|
|
1939
|
+
{
|
|
1940
|
+
// 判据 A 放行侧:入口已确认(台账行 + 指纹配对)→ plan draft 静默(L1 免 spec 档)
|
|
1941
|
+
const T = mkfix();
|
|
1942
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1943
|
+
const fp = 'a'.repeat(64);
|
|
1944
|
+
w(T, 'workflow/intents/2026-09-30-ok.md', INTENT('ok', `状态: approved\n级别: L1\n日期: 2026-09-30\n确认指纹: ${fp.slice(0, 16)}`));
|
|
1945
|
+
w(T, 'workflow/plans/2026-09-30-ok.md', PLAN('ok', '状态: draft\n级别: L1'));
|
|
1946
|
+
w(T, '.agents/confirmations.jsonl', JSON.stringify({ ts: '2026-09-30T01:00:00.000Z', doc: 'workflow/intents/2026-09-30-ok.md', stage: 'approved', fingerprint: fp, prev: 'draft', source: 'chat-delegated', batch: 't1', seq: 1, of: 1 }) + '\n');
|
|
1947
|
+
const r = run(T);
|
|
1948
|
+
check('检查19-A 放行侧:入口 approved + 台账行 → plan draft 静默(L1)',
|
|
1949
|
+
r.status === 0 && !outOf(r).includes('逐阶段'),
|
|
1950
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1951
|
+
rmfix(T);
|
|
1952
|
+
}
|
|
1953
|
+
{
|
|
1954
|
+
// 判据 B:台账顺序倒置(spec 早于 intent)→ 告警;行无对应文件也可判(读台账事实)
|
|
1955
|
+
const T = mkfix();
|
|
1956
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1957
|
+
const row = (doc, ts) => JSON.stringify({ ts, doc, stage: 'approved', fingerprint: 'b'.repeat(64), prev: 'draft' }) + '\n';
|
|
1958
|
+
w(T, '.agents/confirmations.jsonl',
|
|
1959
|
+
row('workflow/intents/2026-09-29-ord.md', '2026-09-29T10:00:00.000Z')
|
|
1960
|
+
+ row('workflow/specs/2026-09-29-ord.md', '2026-09-29T09:00:00.000Z')
|
|
1961
|
+
+ row('workflow/plans/2026-09-29-ord.md', '2026-09-29T11:00:00.000Z'));
|
|
1962
|
+
const r = run(T);
|
|
1963
|
+
check('检查19-B 顺序倒置 → 告警且 exit 0',
|
|
1964
|
+
r.status === 0 && outOf(r).includes('逐阶段') && outOf(r).includes('倒置') && outOf(r).includes('2026-09-29-ord'),
|
|
1965
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
1966
|
+
rmfix(T);
|
|
1967
|
+
}
|
|
1968
|
+
{
|
|
1969
|
+
// 判据 B:顺序合规 → 静默;组内最早 ts 早于 stageGateSince → 整组跳过
|
|
1970
|
+
const T1 = mkfix();
|
|
1971
|
+
w(T1, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1972
|
+
const row1 = (doc, ts) => JSON.stringify({ ts, doc, stage: 'approved', fingerprint: 'c'.repeat(64), prev: 'draft' }) + '\n';
|
|
1973
|
+
w(T1, '.agents/confirmations.jsonl',
|
|
1974
|
+
row1('workflow/intents/2026-09-29-ok2.md', '2026-09-29T09:00:00.000Z')
|
|
1975
|
+
+ row1('workflow/specs/2026-09-29-ok2.md', '2026-09-29T10:00:00.000Z')
|
|
1976
|
+
+ row1('workflow/plans/2026-09-29-ok2.md', '2026-09-29T11:00:00.000Z'));
|
|
1977
|
+
const r1 = run(T1);
|
|
1978
|
+
check('检查19-B 顺序合规 → 静默',
|
|
1979
|
+
r1.status === 0 && !outOf(r1).includes('逐阶段'),
|
|
1980
|
+
`exit=${r1.status}\n${outOf(r1)}`);
|
|
1981
|
+
rmfix(T1);
|
|
1982
|
+
|
|
1983
|
+
const T2 = mkfix();
|
|
1984
|
+
w(T2, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
1985
|
+
const row2 = (doc, ts) => JSON.stringify({ ts, doc, stage: 'approved', fingerprint: 'd'.repeat(64), prev: 'draft' }) + '\n';
|
|
1986
|
+
w(T2, '.agents/confirmations.jsonl',
|
|
1987
|
+
row2('workflow/intents/2026-09-01-h.md', '2026-09-01T10:00:00.000Z')
|
|
1988
|
+
+ row2('workflow/specs/2026-09-01-h.md', '2026-09-01T09:00:00.000Z'));
|
|
1989
|
+
const r2 = run(T2);
|
|
1990
|
+
check('检查19-B 日期门:组内最早 ts 早于 stageGateSince → 整组静默(历史豁免)',
|
|
1991
|
+
r2.status === 0 && !outOf(r2).includes('逐阶段'),
|
|
1992
|
+
`exit=${r2.status}\n${outOf(r2)}`);
|
|
1993
|
+
rmfix(T2);
|
|
1994
|
+
}
|
|
1995
|
+
{
|
|
1996
|
+
// policy v1(无 stageGateSince 键)→ 检查 19 整体跳过
|
|
1997
|
+
const T = mkfix();
|
|
1998
|
+
w(T, '.agents/kit.json', '{"policyVersion":1}\n');
|
|
1999
|
+
const fp = 'e'.repeat(64);
|
|
2000
|
+
w(T, 'workflow/intents/2026-09-30-v1.md', INTENT('v1', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2001
|
+
w(T, 'workflow/plans/2026-09-30-v1.md', PLAN('v1', '状态: draft\n级别: L1'));
|
|
2002
|
+
w(T, '.agents/confirmations.jsonl', JSON.stringify({ ts: '2026-09-29T10:00:00.000Z', doc: 'workflow/specs/2026-09-30-v1.md', stage: 'approved', fingerprint: fp, prev: 'draft' }) + '\n');
|
|
2003
|
+
const r = run(T);
|
|
2004
|
+
check('检查19 policy v1(无键)→ 整体跳过(零逐阶段告警)',
|
|
2005
|
+
r.status === 0 && !outOf(r).includes('逐阶段'),
|
|
2006
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
2007
|
+
rmfix(T);
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
{
|
|
2011
|
+
// 判据 A 的 plan→spec 分支(复核 P2-4②):入口 L2 已确认 + spec 为 draft → plan 出「起草先于 spec 确认」
|
|
2012
|
+
const T = mkfix();
|
|
2013
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2014
|
+
const fp3 = '9'.repeat(64);
|
|
2015
|
+
w(T, 'workflow/intents/2026-09-30-ps.md', INTENT('ps', `状态: approved\n级别: L2\n日期: 2026-09-30\n确认指纹: ${fp3.slice(0, 16)}`));
|
|
2016
|
+
w(T, 'workflow/specs/2026-09-30-ps.md', SPEC('ps', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2017
|
+
w(T, 'workflow/plans/2026-09-30-ps.md', PLAN('ps', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2018
|
+
w(T, '.agents/confirmations.jsonl', JSON.stringify({ ts: '2026-09-30T03:00:00.000Z', doc: 'workflow/intents/2026-09-30-ps.md', stage: 'approved', fingerprint: fp3, prev: 'draft', source: 'chat-delegated', batch: 'ps', seq: 1, of: 1 }) + '\n');
|
|
2019
|
+
const r = run(T);
|
|
2020
|
+
check('检查19-A plan→spec 分支:入口确认但 spec 未确认 → 「起草先于 spec 确认」',
|
|
2021
|
+
r.status === 0 && outOf(r).includes('起草先于 spec 确认'),
|
|
2022
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
2023
|
+
rmfix(T);
|
|
2024
|
+
}
|
|
2025
|
+
{
|
|
2026
|
+
// 交叉一致性(复核 P2-8):stage-gates.entryConfirmed 与检查 19 内联判据同口径——反例:
|
|
2027
|
+
// approved 但无台账行且日期 ≥ 生效日 → 两处皆判「未确认」(check15 亦会 hard-block,断言只取逐阶段警示)
|
|
2028
|
+
const T = mkfix();
|
|
2029
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2030
|
+
w(T, 'workflow/intents/2026-09-30-x1.md', INTENT('x1', '状态: approved\n级别: L1\n日期: 2026-09-30'));
|
|
2031
|
+
w(T, 'workflow/plans/2026-09-30-x1.md', PLAN('x1', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2032
|
+
const e1 = entryConfirmed(T, '2026-09-30-x1');
|
|
2033
|
+
const r1 = run(T);
|
|
2034
|
+
check('交叉一致性①:入口 approved 无台账行 → stage-gates 判未确认 ∧ 检查 19 出「起草先于入口确认」',
|
|
2035
|
+
e1.ok === false && outOf(r1).includes('起草先于入口确认'),
|
|
2036
|
+
`sg.ok=${e1.ok}\n${outOf(r1)}`);
|
|
2037
|
+
rmfix(T);
|
|
2038
|
+
|
|
2039
|
+
// 正例:approved + 台账行 → 两处皆判「已确认」(检查 19 零「逐阶段」)
|
|
2040
|
+
const T2 = mkfix();
|
|
2041
|
+
w(T2, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2042
|
+
const fp2 = 'f'.repeat(64);
|
|
2043
|
+
w(T2, 'workflow/intents/2026-09-30-x2.md', INTENT('x2', `状态: approved\n级别: L1\n日期: 2026-09-30\n确认指纹: ${fp2.slice(0, 16)}`));
|
|
2044
|
+
w(T2, 'workflow/plans/2026-09-30-x2.md', PLAN('x2', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2045
|
+
w(T2, '.agents/confirmations.jsonl', JSON.stringify({ ts: '2026-09-30T02:30:00.000Z', doc: 'workflow/intents/2026-09-30-x2.md', stage: 'approved', fingerprint: fp2, prev: 'draft', source: 'chat-delegated', batch: 'x2', seq: 1, of: 1 }) + '\n');
|
|
2046
|
+
const e2 = entryConfirmed(T2, '2026-09-30-x2');
|
|
2047
|
+
const r2 = run(T2);
|
|
2048
|
+
check('交叉一致性②:入口 approved + 台账行 → stage-gates 判已确认 ∧ 检查 19 零「逐阶段」',
|
|
2049
|
+
e2.ok === true && !outOf(r2).includes('逐阶段'),
|
|
2050
|
+
`sg.ok=${e2.ok}\n${outOf(r2)}`);
|
|
2051
|
+
rmfix(T2);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
{
|
|
2055
|
+
// N1 存量短路(检查 19 同口径):legacy 入口(无级别)+ plan draft → 零「逐阶段」告警
|
|
2056
|
+
const T = mkfix();
|
|
2057
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2058
|
+
w(T, 'workflow/intents/2026-09-30-leg19.md', INTENT('leg19', '状态: approved\n日期: 2026-01-01\n流程: legacy'));
|
|
2059
|
+
w(T, 'workflow/plans/2026-09-30-leg19.md', PLAN('leg19', '状态: draft\n流程: legacy'));
|
|
2060
|
+
const r = run(T);
|
|
2061
|
+
check('检查19 存量短路:legacy 入口无级别 → plan draft 零「逐阶段」告警',
|
|
2062
|
+
r.status === 0 && !outOf(r).includes('逐阶段'),
|
|
2063
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
2064
|
+
rmfix(T);
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
{
|
|
2068
|
+
// 检查 19 判据 A 的 **incident 入口**分支(第五轮复核 P2:该分支此前零形态覆盖——
|
|
2069
|
+
// 注入验证证明:把 MARK_RE19 改为恒 false,套件仍全绿 → 正则改动不会被拦下)
|
|
2070
|
+
{
|
|
2071
|
+
const T = mkfix();
|
|
2072
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2073
|
+
// 正例:incident(open + 时间线「用户确认」条目)+ spec draft → 零「逐阶段」告警
|
|
2074
|
+
// 注:incident 亦受配对门约束(须有同名 plan),故夹具须补 plan,否则 exit 1 遮住本判据
|
|
2075
|
+
w(T, 'workflow/incidents/2026-09-30-ic.md', '---\n状态: open\n级别: L2\n发现: 2026-09-30\n---\n# INCIDENT\n\n## 时间线\n- 用户确认:草稿过目通过(2026-09-30)\n');
|
|
2076
|
+
w(T, 'workflow/specs/2026-09-30-ic.md', SPEC('ic', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2077
|
+
w(T, 'workflow/plans/2026-09-30-ic.md', PLAN('ic', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2078
|
+
const r1 = run(T);
|
|
2079
|
+
check('检查19-A incident 入口(时间线留痕)→ 不告警「起草先于入口确认」',
|
|
2080
|
+
r1.status === 0 && !outOf(r1).includes('起草先于入口确认'),
|
|
2081
|
+
`exit=${r1.status}\n${outOf(r1)}`);
|
|
2082
|
+
rmfix(T);
|
|
2083
|
+
|
|
2084
|
+
// 负例:叙述句不算留痕 → 出「起草先于入口确认」
|
|
2085
|
+
const T2 = mkfix();
|
|
2086
|
+
w(T2, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2087
|
+
w(T2, 'workflow/incidents/2026-09-30-ic2.md', '---\n状态: open\n级别: L2\n发现: 2026-09-30\n---\n# INCIDENT\n\n## 时间线\n- 2026-09-30 用户确认了方案(叙述)\n');
|
|
2088
|
+
w(T2, 'workflow/specs/2026-09-30-ic2.md', SPEC('ic2', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2089
|
+
w(T2, 'workflow/plans/2026-09-30-ic2.md', PLAN('ic2', '状态: draft\n级别: L2\n日期: 2026-09-30'));
|
|
2090
|
+
const r2 = run(T2);
|
|
2091
|
+
check('检查19-A incident 入口(叙述句不算)→ 出「起草先于入口确认」',
|
|
2092
|
+
r2.status === 0 && outOf(r2).includes('起草先于入口确认'),
|
|
2093
|
+
`exit=${r2.status}\n${outOf(r2)}`);
|
|
2094
|
+
rmfix(T2);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
// ---- 风险泳道(2026-09-30 hybrid-governance-risk-lanes):L0 配对豁免 + 红线判低 ----
|
|
2099
|
+
{
|
|
2100
|
+
// L0 intent 无 plan 无 spec → 协作道配对豁免(异步审计兜底)
|
|
2101
|
+
const T = mkfix();
|
|
2102
|
+
w(T, 'workflow/intents/2026-09-12-l0.md', INTENT('l0', '状态: approved\n级别: L0\nrisk_level: L0\n日期: 2026-09-12'));
|
|
2103
|
+
const r = run(T);
|
|
2104
|
+
check('风险泳道:L0 intent 缺 plan/spec → 配对豁免 exit 0',
|
|
2105
|
+
r.status === 0 && !outOf(r).includes('配对断裂'),
|
|
2106
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
2107
|
+
rmfix(T);
|
|
2108
|
+
}
|
|
2109
|
+
{
|
|
2110
|
+
// L1 intent 仍须 plan(协作道不豁免 L1——防倒退回归)
|
|
2111
|
+
const T1 = mkfix();
|
|
2112
|
+
w(T1, 'workflow/intents/2026-09-12-l1.md', INTENT('l1', '状态: approved\n级别: L1\nrisk_level: L1\n日期: 2026-09-12'));
|
|
2113
|
+
expectHard('风险泳道:L1 intent 缺 plan 仍 hard 配对断裂', T1, '配对断裂');
|
|
2114
|
+
rmfix(T1);
|
|
2115
|
+
|
|
2116
|
+
// L0/L1 勾触达红线 → hard「红线判低」(级别与 risk_level 任一判定皆拦)
|
|
2117
|
+
const T2 = mkfix();
|
|
2118
|
+
w(T2, 'workflow/intents/2026-09-12-rl.md', INTENT('rl', '状态: approved\n级别: L1\nrisk_level: L1\n日期: 2026-09-12',
|
|
2119
|
+
'\n## 触达红线(对照 AGENTS.md)\n- [x] 规则 / 契约变更(接口签名)→ 级别至少 L2\n'));
|
|
2120
|
+
const r2 = run(T2);
|
|
2121
|
+
check('风险泳道:L1 勾触达红线 → hard 红线判低',
|
|
2122
|
+
r2.status === 1 && outOf(r2).includes('红线判低'),
|
|
2123
|
+
`exit=${r2.status}\n${outOf(r2)}`);
|
|
2124
|
+
rmfix(T2);
|
|
2125
|
+
|
|
2126
|
+
const T3 = mkfix();
|
|
2127
|
+
w(T3, 'workflow/intents/2026-09-12-rl2.md', INTENT('rl2', '状态: approved\n级别: L2\nrisk_level: L2\n日期: 2026-09-12',
|
|
2128
|
+
'\n## 触达红线(对照 AGENTS.md)\n- [x] 规则 / 契约变更(接口签名)→ 级别至少 L2\n'));
|
|
2129
|
+
w(T3, 'workflow/specs/2026-09-12-rl2.md', SPEC('rl2', '状态: approved\n级别: L2'));
|
|
2130
|
+
w(T3, 'workflow/plans/2026-09-12-rl2.md', PLAN('rl2', '状态: approved\n级别: L2'));
|
|
2131
|
+
const r3 = run(T3);
|
|
2132
|
+
check('风险泳道:L2 勾红线不判低(防御道受理)',
|
|
2133
|
+
r3.status === 0 && !outOf(r3).includes('红线判低'),
|
|
2134
|
+
`exit=${r3.status}\n${outOf(r3)}`);
|
|
2135
|
+
rmfix(T3);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// ---- 检查 15 并录豁免:--batch sanctioned 批次(delegated of>1 + 全行 brief:true)→ 零「确认并录」----
|
|
2139
|
+
{
|
|
2140
|
+
const T = mkfix();
|
|
2141
|
+
const fpI = 'a'.repeat(64);
|
|
2142
|
+
const fpP = 'b'.repeat(64);
|
|
2143
|
+
w(T, 'workflow/intents/2026-09-12-b1.md', INTENT('b1', `状态: approved\n级别: L1\n日期: 2026-09-12\n确认指纹: ${fpI.slice(0, 16)}`));
|
|
2144
|
+
w(T, 'workflow/plans/2026-09-12-b1.md', PLAN('b1', `状态: approved\n级别: L1\n日期: 2026-09-12\n确认指纹: ${fpP.slice(0, 16)}`));
|
|
2145
|
+
const row = (doc, fp, seq) => JSON.stringify({ ts: '2026-09-29T02:00:00.000Z', doc, stage: 'approved', fingerprint: fp, prev: 'draft', source: 'chat-delegated', quote: '批量放行', batch: 'bt1', seq, of: 2, brief: true }) + '\n';
|
|
2146
|
+
w(T, '.agents/confirmations.jsonl',
|
|
2147
|
+
row('workflow/intents/2026-09-12-b1.md', fpI, 1)
|
|
2148
|
+
+ row('workflow/plans/2026-09-12-b1.md', fpP, 2));
|
|
2149
|
+
const r = run(T);
|
|
2150
|
+
check('检查15 --batch sanctioned:全 brief 批次 → 零「确认并录」告警',
|
|
2151
|
+
r.status === 0 && !outOf(r).includes('确认并录'),
|
|
2152
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
2153
|
+
rmfix(T);
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
// ---- 检查 19 协作道豁免(2026-09-30 hybrid-governance-risk-lanes)----
|
|
2157
|
+
{
|
|
2158
|
+
// L1 intent draft + plan draft(日期 ≥ stageGateSince)→ 「先动手后确认」是泳道语义,零告警
|
|
2159
|
+
const T = mkfix();
|
|
2160
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2161
|
+
w(T, 'workflow/intents/2026-09-30-lane.md', INTENT('lane', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2162
|
+
w(T, 'workflow/plans/2026-09-30-lane.md', PLAN('lane', '状态: draft\n级别: L1'));
|
|
2163
|
+
const r = run(T);
|
|
2164
|
+
check('检查19 协作道豁免:L1 intent draft + plan draft → 零「起草先于入口确认」',
|
|
2165
|
+
r.status === 0 && !outOf(r).includes('起草先于入口确认'),
|
|
2166
|
+
`exit=${r.status}\n${outOf(r)}`);
|
|
2167
|
+
rmfix(T);
|
|
2168
|
+
}
|
|
2169
|
+
{
|
|
2170
|
+
// 防倒退:L1 intent approved 却无台账行(非 draft)→ 仍告警(豁免仅限 draft 态,防手改状态冒充确认)
|
|
2171
|
+
const T = mkfix();
|
|
2172
|
+
w(T, '.agents/kit.json', '{"policyVersion":2}\n');
|
|
2173
|
+
w(T, 'workflow/intents/2026-09-30-lane2.md', INTENT('lane2', '状态: approved\n级别: L1\n日期: 2026-09-30'));
|
|
2174
|
+
w(T, 'workflow/plans/2026-09-30-lane2.md', PLAN('lane2', '状态: draft\n级别: L1'));
|
|
2175
|
+
const r = run(T);
|
|
2176
|
+
check('检查19 L1 approved 无台账行 → 仍出「起草先于入口确认」(豁免仅限 draft)',
|
|
2177
|
+
outOf(r).includes('起草先于入口确认'),
|
|
2178
|
+
outOf(r));
|
|
2179
|
+
rmfix(T);
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
// ---- 加固门(2026-09-30 hybrid-governance-explore-hardening;仅 --hardening 启用,不占 1-19 编号)----
|
|
2183
|
+
{
|
|
2184
|
+
// H1:未标记 exploring 的 draft 件 → 带/不带 --hardening 均不拦(本门只管「已标记未收口」,
|
|
2185
|
+
// 常规流程的 draft 中间态不受影响——intent approved 后 plan 起草属正常在途)
|
|
2186
|
+
const T = mkfix();
|
|
2187
|
+
w(T, 'workflow/intents/2026-09-30-h0.md', INTENT('h0', '状态: draft\n级别: L1\n日期: 2026-09-30'));
|
|
2188
|
+
w(T, 'workflow/plans/2026-09-30-h0.md', PLAN('h0', '状态: draft\n级别: L1'));
|
|
2189
|
+
const rOff = run(T);
|
|
2190
|
+
const rOn = run(T, { args: ['--hardening'] });
|
|
2191
|
+
check('加固门 H1:未标记 exploring 的 draft 件 → 带/不带 --hardening 均 exit 0',
|
|
2192
|
+
rOff.status === 0 && rOn.status === 0, `off=${rOff.status}\non=${outOf(rOn)}`);
|
|
2193
|
+
rmfix(T);
|
|
2194
|
+
}
|
|
2195
|
+
{
|
|
2196
|
+
// H2:已标记 exploring、intent 仍 draft(配同名 plan 防 check 1 配对拦)→ --hardening hard 拦;
|
|
2197
|
+
// 不带旗标 exit 0(泳道内推送/日常扫描不受影响)
|
|
2198
|
+
const T = mkfix();
|
|
2199
|
+
w(T, 'workflow/intents/2026-09-30-h1.md', INTENT('h1', '状态: draft\n级别: L1\nrisk_level: L1\n阶段: exploring\n日期: 2026-09-30'));
|
|
2200
|
+
w(T, 'workflow/plans/2026-09-30-h1.md', PLAN('h1', '状态: draft\n级别: L1'));
|
|
2201
|
+
const rOn = run(T, { args: ['--hardening'] });
|
|
2202
|
+
const rOff = run(T);
|
|
2203
|
+
check('加固门 H2:exploring 件 intent 仍 draft → --hardening exit 1「加固未过」;不带旗标 exit 0',
|
|
2204
|
+
rOn.status === 1 && outOf(rOn).includes('加固未过') && rOff.status === 0, outOf(rOn));
|
|
2205
|
+
rmfix(T);
|
|
2206
|
+
}
|
|
2207
|
+
{
|
|
2208
|
+
// H3:已收口(intent approved + plan approved,均带真实指纹 + 台账配对防检查 15 先拦)→ 放行;L1 不索 spec
|
|
2209
|
+
const T = mkfix();
|
|
2210
|
+
const ent = mkConfirmedDoc(T, 'workflow/intents/2026-09-30-h2.md', '状态: approved\n级别: L1\nrisk_level: L1\n阶段: exploring\n日期: 2026-09-30');
|
|
2211
|
+
const plan = mkConfirmedDoc(T, 'workflow/plans/2026-09-30-h2.md', '状态: approved\n级别: L1\n阶段: exploring');
|
|
2212
|
+
writeLedger(T, [
|
|
2213
|
+
{ ts: '2026-09-30T01:00:00.000Z', doc: ent.rel, stage: 'approved', fingerprint: ent.fp, prev: 'draft', source: 'tty' },
|
|
2214
|
+
{ ts: '2026-09-30T02:00:00.000Z', doc: plan.rel, stage: 'approved', fingerprint: plan.fp, prev: 'draft', source: 'tty' },
|
|
2215
|
+
]);
|
|
2216
|
+
const r = run(T, { args: ['--hardening'] });
|
|
2217
|
+
check('加固门 H3:exploring 件已收口(intent/plan approved + 台账配对)→ --hardening exit 0',
|
|
2218
|
+
r.status === 0, outOf(r));
|
|
2219
|
+
rmfix(T);
|
|
2220
|
+
}
|
|
2221
|
+
{
|
|
2222
|
+
// H4:intent 已收口但 plan 仍 draft → hard「加固未过」(plan 未收口;L2 缺 spec 由检查 1 同步出账)
|
|
2223
|
+
const T = mkfix();
|
|
2224
|
+
const ent = mkConfirmedDoc(T, 'workflow/intents/2026-09-30-h3.md', '状态: approved\n级别: L2\n阶段: exploring\n日期: 2026-09-30');
|
|
2225
|
+
writeLedger(T, [{ ts: '2026-09-30T01:00:00.000Z', doc: ent.rel, stage: 'approved', fingerprint: ent.fp, prev: 'draft', source: 'tty' }]);
|
|
2226
|
+
w(T, 'workflow/plans/2026-09-30-h3.md', PLAN('h3', '状态: draft\n级别: L2'));
|
|
2227
|
+
const r = run(T, { args: ['--hardening'] });
|
|
2228
|
+
check('加固门 H4:exploring 件 plan 未收口(draft)→ --hardening exit 1「加固未过」',
|
|
2229
|
+
r.status === 1 && outOf(r).includes('加固未过'), outOf(r));
|
|
2230
|
+
rmfix(T);
|
|
2231
|
+
}
|
|
2232
|
+
{
|
|
2233
|
+
// H5:放弃态出列——intent 已 superseded 的 exploring 件 → --hardening 不拦(探索作废留档,无代码随行)。
|
|
2234
|
+
// 台账指纹按 confirm-doc 前向语义复刻:fp = 跳转前(approved 态)内容的指纹,当前盘面 superseded + 指纹行
|
|
2235
|
+
// ——检查 15 的内容绑定(superseded 受绑)按 prev=approved 复原重算须能对上。
|
|
2236
|
+
const T = mkfix();
|
|
2237
|
+
const rel = 'workflow/intents/2026-09-30-h4.md';
|
|
2238
|
+
const prevText = '---\n状态: approved\n级别: L1\n阶段: exploring\n日期: 2026-09-30\n---\n# DOC\n';
|
|
2239
|
+
const fp = computeFingerprint(prevText);
|
|
2240
|
+
fs.writeFileSync(path.join(T, rel), `---\n状态: superseded\n级别: L1\n阶段: exploring\n日期: 2026-09-30\n确认指纹: ${fp.slice(0, 16)}\n---\n# DOC\n`);
|
|
2241
|
+
w(T, 'workflow/plans/2026-09-30-h4.md', PLAN('h4', '状态: draft\n级别: L1')); // 防 check 1 配对拦(L1 须 plan;draft 仅 warning 不碍 exit 0)
|
|
2242
|
+
writeLedger(T, [{ ts: '2026-09-30T03:00:00.000Z', doc: rel, stage: 'superseded', fingerprint: fp, prev: 'approved', source: 'tty' }]);
|
|
2243
|
+
const r = run(T, { args: ['--hardening'] });
|
|
2244
|
+
check('加固门 H5:exploring 件显式放弃(superseded + 台账绑定)→ --hardening exit 0(出列不加固)',
|
|
2245
|
+
r.status === 0, outOf(r));
|
|
2246
|
+
rmfix(T);
|
|
2247
|
+
}
|
|
2248
|
+
{
|
|
2249
|
+
// H6:未知参数 → 用法提示 exit 1(arg 解析收紧后的防倒退)
|
|
2250
|
+
const T = mkfix();
|
|
2251
|
+
const r = run(T, { args: ['--bogus'] });
|
|
2252
|
+
check('加固门 H6:未知 CLI 参数 → exit 1 用法提示', r.status === 1 && /用法/.test(outOf(r)), outOf(r));
|
|
2253
|
+
rmfix(T);
|
|
2254
|
+
}
|
|
2255
|
+
|
|
1596
2256
|
console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
|
|
1597
2257
|
process.exit(fail ? 1 : 0);
|