agentic-flow-kit 1.7.16 → 1.8.0
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 +11 -7
- package/modules/hosts/claude/commands/wf-build.md +1 -1
- package/modules/hosts/claude/commands/wf-maintain.md +1 -1
- package/modules/hosts/claude/commands/wf-new-task.md +9 -8
- package/modules/hosts/claude/commands/wf-plan.md +1 -1
- package/modules/hosts/claude/commands/wf-review.md +1 -1
- package/modules/hosts/claude/commands/wf-test.md +1 -1
- package/modules/hosts/codex/commands/wf-build.md +1 -1
- package/modules/hosts/codex/commands/wf-maintain.md +1 -1
- package/modules/hosts/codex/commands/wf-new-task.md +9 -8
- package/modules/hosts/codex/commands/wf-plan.md +1 -1
- package/modules/hosts/codex/commands/wf-review.md +1 -1
- package/modules/hosts/codex/commands/wf-test.md +1 -1
- package/modules/hosts/cursor/commands/wf-build.md +1 -1
- package/modules/hosts/cursor/commands/wf-maintain.md +1 -1
- package/modules/hosts/cursor/commands/wf-new-task.md +9 -8
- package/modules/hosts/cursor/commands/wf-plan.md +1 -1
- package/modules/hosts/cursor/commands/wf-review.md +1 -1
- package/modules/hosts/cursor/commands/wf-test.md +1 -1
- package/modules/hosts/opencode/commands/wf-build.md +1 -1
- package/modules/hosts/opencode/commands/wf-maintain.md +1 -1
- package/modules/hosts/opencode/commands/wf-new-task.md +9 -8
- package/modules/hosts/opencode/commands/wf-plan.md +1 -1
- package/modules/hosts/opencode/commands/wf-review.md +1 -1
- package/modules/hosts/opencode/commands/wf-test.md +1 -1
- package/modules/hosts/trae/commands/wf-build.md +1 -1
- package/modules/hosts/trae/commands/wf-maintain.md +1 -1
- package/modules/hosts/trae/commands/wf-new-task.md +9 -8
- package/modules/hosts/trae/commands/wf-plan.md +1 -1
- package/modules/hosts/trae/commands/wf-review.md +1 -1
- package/modules/hosts/trae/commands/wf-test.md +1 -1
- package/package.json +1 -1
- package/src/cli.mjs +6 -2
- package/src/doctor.mjs +11 -2
- package/src/executor.mjs +25 -8
- package/src/executor.test.mjs +4 -1
- package/src/fresh-init.test.mjs +1 -1
- package/src/init.mjs +20 -3
- package/src/profiles.mjs +7 -3
- package/src/rule-budget.test.mjs +2 -2
- package/src/stack-profile.test.mjs +6 -0
- package/src/validate.mjs +6 -1
- package/src/validate.test.mjs +1 -1
- package/templates/.gitattributes +4 -1
- package/templates/AGENTS.md +2 -2
- package/templates/_agents/commands/build.md +1 -1
- package/templates/_agents/commands/maintain.md +1 -1
- package/templates/_agents/commands/new-task.md +9 -8
- package/templates/_agents/commands/plan.md +1 -1
- package/templates/_agents/commands/review.md +1 -1
- package/templates/_agents/commands/test.md +1 -1
- package/templates/_agents/engine-lock.json +130 -42
- package/templates/_agents/hooks/check-index-incremental.mjs +48 -0
- package/templates/_agents/hooks/check-pairing-incremental.mjs +66 -0
- package/templates/_agents/hooks/check-wiki-ledger.mjs +30 -0
- package/templates/_agents/lane-surfaces.txt +4 -1
- package/templates/_agents/level-hints.txt +1 -1
- package/templates/_agents/loop/papercut-debt/DESIGN.md +112 -0
- package/templates/_agents/mechanism-registry.md +27 -0
- package/templates/_agents/rule-budgets.txt +2 -2
- package/templates/_agents/scripts/agg-delegations.cjs +21 -3
- package/templates/_agents/scripts/agg-gate-stats.mjs +3 -3
- package/templates/_agents/scripts/agg-judgments.mjs +80 -0
- package/templates/_agents/scripts/agg-judgments.test.mjs +45 -0
- package/templates/_agents/scripts/archive-sweep.mjs +233 -0
- package/templates/_agents/scripts/archive-sweep.test.mjs +129 -0
- package/templates/_agents/scripts/check-attribution.mjs +37 -0
- package/templates/_agents/scripts/check-convergence.mjs +119 -0
- package/templates/_agents/scripts/check-engine-integrity.mjs +4 -1
- package/templates/_agents/scripts/check-funnel.mjs +56 -0
- package/templates/_agents/scripts/check-hygiene.mjs +5 -5
- package/templates/_agents/scripts/check-index-incremental.test.mjs +73 -0
- package/templates/_agents/scripts/check-lane-surface.mjs +2 -2
- package/templates/_agents/scripts/check-lane-surface.test.mjs +5 -5
- package/templates/_agents/scripts/check-ledger-invariant.mjs +403 -152
- package/templates/_agents/scripts/check-ledger-invariant.test.mjs +433 -3
- package/templates/_agents/scripts/check-loop-rev.test.mjs +8 -5
- package/templates/_agents/scripts/check-loop-unit.test.mjs +8 -0
- package/templates/_agents/scripts/check-loop.mjs +215 -34
- package/templates/_agents/scripts/check-loop.test.mjs +570 -49
- package/templates/_agents/scripts/check-prejudgment.mjs +59 -0
- package/templates/_agents/scripts/check-registry.mjs +45 -0
- package/templates/_agents/scripts/check-scope.mjs +96 -0
- package/templates/_agents/scripts/check-scope.test.mjs +96 -0
- package/templates/_agents/scripts/check-scripts-syntax.mjs +61 -0
- package/templates/_agents/scripts/check-scripts-syntax.test.mjs +72 -0
- package/templates/_agents/scripts/check-spec-verif.mjs +47 -0
- package/templates/_agents/scripts/compact-ledgers.mjs +3 -3
- package/templates/_agents/scripts/confirm-doc.mjs +114 -38
- package/templates/_agents/scripts/confirm-doc.test.mjs +180 -4
- package/templates/_agents/scripts/edit-face-check.mjs +5 -3
- package/templates/_agents/scripts/fill-intent.mjs +5 -3
- package/templates/_agents/scripts/fill-intent.test.mjs +19 -1
- package/templates/_agents/scripts/fill-plan.mjs +10 -6
- package/templates/_agents/scripts/fill-plan.test.mjs +19 -3
- package/templates/_agents/scripts/fill-spec.mjs +6 -3
- package/templates/_agents/scripts/fill-spec.test.mjs +12 -4
- package/templates/_agents/scripts/fixture-scripts.mjs +2 -2
- package/templates/_agents/scripts/fixture-scripts.test.mjs +6 -1
- package/templates/_agents/scripts/gate-checklist.mjs +6 -0
- package/templates/_agents/scripts/gate-seg.mjs +1 -1
- package/templates/_agents/scripts/gen-effective-policy.mjs +107 -0
- package/templates/_agents/scripts/gen-effective-policy.test.mjs +100 -0
- package/templates/_agents/scripts/gen-verify-evidence.mjs +107 -0
- package/templates/_agents/scripts/gen-verify-evidence.test.mjs +84 -0
- package/templates/_agents/scripts/gen-workflow-dashboard.mjs +8 -3
- package/templates/_agents/scripts/gen-workflow-dashboard.test.mjs +2 -1
- package/templates/_agents/scripts/gen-workflow-metrics.mjs +3 -3
- package/templates/_agents/scripts/git-core.mjs +45 -0
- package/templates/_agents/scripts/judgment-ledger.mjs +65 -0
- package/templates/_agents/scripts/judgment-ledger.test.mjs +129 -0
- package/templates/_agents/scripts/kb-search.mjs +19 -4
- package/templates/_agents/scripts/kb-search.test.mjs +33 -0
- package/templates/_agents/scripts/lane-surface-audit.mjs +143 -0
- package/templates/_agents/scripts/lane-surface-audit.test.mjs +93 -0
- package/templates/_agents/scripts/ledger-shards.mjs +307 -0
- package/templates/_agents/scripts/ledger-shards.test.mjs +339 -0
- package/templates/_agents/scripts/level-criteria.mjs +9 -10
- package/templates/_agents/scripts/level-criteria.test.mjs +7 -5
- package/templates/_agents/scripts/papercut-debt-controller.mjs +141 -0
- package/templates/_agents/scripts/papercut-debt-controller.test.mjs +127 -0
- package/templates/_agents/scripts/papercut-debt-sensor.mjs +170 -0
- package/templates/_agents/scripts/papercut-debt-sensor.test.mjs +146 -0
- package/templates/_agents/scripts/pipeline-run.mjs +6 -22
- package/templates/_agents/scripts/pipeline-run.test.mjs +22 -42
- package/templates/_agents/scripts/policy.mjs +28 -0
- package/templates/_agents/scripts/resolve-merge-conflicts.mjs +110 -0
- package/templates/_agents/scripts/resolve-merge-conflicts.test.mjs +87 -0
- package/templates/_agents/scripts/rewrite-file.mjs +133 -0
- package/templates/_agents/scripts/rewrite-file.test.mjs +148 -0
- package/templates/_agents/scripts/rule-budget.mjs +83 -0
- package/templates/_agents/scripts/run-tests.mjs +99 -0
- package/templates/_agents/scripts/run-tests.test.mjs +105 -0
- package/templates/_agents/scripts/solidify-task.test.mjs +1 -1
- package/templates/_agents/scripts/stage-gates.mjs +34 -12
- package/templates/_agents/scripts/trust-mode.mjs +4 -4
- package/templates/_agents/scripts/trust-mode.test.mjs +1 -1
- package/templates/_agents/scripts/verify.mjs +135 -58
- package/templates/_agents/scripts/verify.test.mjs +156 -47
- package/templates/_agents/scripts/wf-journal.mjs +8 -4
- package/templates/_agents/scripts/workflow-board-server.test.mjs +2 -2
- package/templates/_agents/scripts/workflow-enums.test.mjs +14 -5
- package/templates/_agents/scripts-test-exempt.txt +1 -0
- package/templates/_agents/skills/papercut-fix/SKILL.md +60 -0
- package/templates/_agents/skills/papercut-fix/references/response-template.md +81 -0
- package/templates/_agents/workflow-enums.txt +8 -1
- package/templates/_githooks/pre-commit +110 -90
- package/templates/_githooks/pre-merge-commit +27 -23
- package/templates/_githooks/pre-push +4 -3
- package/templates/_github/workflows/kit-ci.yml +4 -3
- package/templates/workflow/README.md +14 -7
- package/templates/workflow/incidents/_TEMPLATE.md +4 -1
- package/templates/workflow/intents/_TEMPLATE.md +4 -2
- package/templates/workflow/papercuts.md +2 -0
- package/templates/workflow/plans/_TEMPLATE.md +10 -5
- package/templates/workflow/regression-checklist.md +1 -0
- package/templates/workflow/specs/_TEMPLATE.md +17 -1
- package/templates/_agents/hooks/check-pairing-incremental.sh +0 -43
- package/templates/_agents/hooks/check-wiki-ledger.sh +0 -18
- package/templates/_agents/scripts/rule-budget.sh +0 -74
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
// 落态记账——免 TTY、免逐份问答,但台账行如实记 source:"chat-delegated" + quote:<原话>,
|
|
14
14
|
// 永不伪装 TTY 行。调用纪律(AI 须先取得用户对话内明确确认)由 AGENTS.md 确认门条款承载,
|
|
15
15
|
// quote 原话入账供事后对质。
|
|
16
|
-
//
|
|
17
|
-
//
|
|
16
|
+
// 台账:默认分片 .agents/confirmations.d/<doc基名>.jsonl(2026-10-13 ledger-shard-default,
|
|
17
|
+
// 协议对齐 Shipyard 参考实现;--no-shard / CONFIRM_SHARDED=0 回退主台账 .agents/confirmations.jsonl)
|
|
18
|
+
// ——两者入 git、追加式。每行 {ts, doc, stage, fingerprint(64位), prev, source:"tty"|"chat-delegated",
|
|
19
|
+
// quote?(仅委托行), batch, seq, of}。
|
|
18
20
|
// batch/seq/of(2026-09-28 batch-ledger-audit 新增,纯增字段、向后兼容、历史行不回填):
|
|
19
21
|
// 记「本次进程调用落了几份态」这一**写入时确定已知的事实**——batch 为本次调用生成的短随机串
|
|
20
22
|
// (无时间语义、无全局唯一要求,仅需单台账内不碰撞),seq 为该次调用内件序(从 1),of 为本次
|
|
@@ -23,12 +25,12 @@
|
|
|
23
25
|
// 指纹:内容 CRLF 归一 → 剔除「确认指纹:」行(防自引用)→ sha256;frontmatter 存前 16 位,台账存全量。
|
|
24
26
|
// 用法:node .agents/scripts/confirm-doc.mjs <workflow/intents|x.md> [<doc2>...] [--root <仓库根>]
|
|
25
27
|
// node .agents/scripts/confirm-doc.mjs <doc...> --delegated "<用户对话原话>"(委托代录)
|
|
26
|
-
// node .agents/scripts/confirm-doc.mjs <doc...> --delegated "<原话>" --batch(协作道批量:
|
|
28
|
+
// node .agents/scripts/confirm-doc.mjs <doc...> --delegated "<原话>" --batch(协作道批量:L1 多份一次代录)
|
|
27
29
|
// node .agents/scripts/confirm-doc.mjs <doc...> --to superseded|cancelled(放弃态:取代/取消,留指纹与台账)
|
|
28
|
-
// node .agents/scripts/confirm-doc.mjs <doc...> --shard
|
|
29
|
-
//
|
|
30
|
+
// node .agents/scripts/confirm-doc.mjs <doc...> --no-shard(回退主台账 .agents/confirmations.jsonl:
|
|
31
|
+
// 分片已是默认写入口径,环境变量 CONFIRM_SHARDED=0 同效;里程碑可选跑 compact-ledgers.mjs 归并分片)
|
|
30
32
|
// 多文档一次传入:TTY 模式天然逐份过目键入;--delegated 默认一次仅一份(2026-09-27
|
|
31
|
-
// confirm-gate-one-per-call:多份并录曾系统性塌掉 build.md「逐件确认」三道门),
|
|
33
|
+
// confirm-gate-one-per-call:多份并录曾系统性塌掉 build.md「逐件确认」三道门),L1 协作道可
|
|
32
34
|
// `--batch` 多份一次代录(2026-09-30 hybrid-governance-risk-lanes):逐份读「级别」,L2/L3 逐份拒绝
|
|
33
35
|
// (防御道不批量);台账行带 `brief:true` 简洁审计标记(quote 原话仍入账供对质),check-loop 检查 15
|
|
34
36
|
// 并录审计对全行带标记的批次豁免「确认并录」告警。--auto 一次仍仅一份(自治放行不批量)。
|
|
@@ -40,8 +42,8 @@ import fs from 'node:fs';
|
|
|
40
42
|
import path from 'node:path';
|
|
41
43
|
import readline from 'node:readline';
|
|
42
44
|
import { createHash, randomBytes } from 'node:crypto';
|
|
43
|
-
import { spawnSync } from 'node:child_process';
|
|
44
|
-
import { readLedgerEntries } from './ledger-loader.mjs';
|
|
45
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
46
|
+
import { readLedgerEntries, readLedgerRows } from './ledger-loader.mjs';
|
|
45
47
|
import { confirmGateFor, clarifyGateFor, doneGateFor, laneOfEntry, laneOfDoc } from './stage-gates.mjs';
|
|
46
48
|
import { loadKitPolicy, hasFreshVerifyLine, ledgerChainHash, ledgerDocChainActive, ledgerDocChainPrev, scopeDigest } from './policy.mjs';
|
|
47
49
|
|
|
@@ -158,10 +160,12 @@ export function applyTransition(text, targetStage, fingerprint16) {
|
|
|
158
160
|
// 最后一个带 hash 行**(ledgerDocChainPrev),不再取物理末行。理由见 POLICIES v8 注释:物理链隐含
|
|
159
161
|
// 「单写者顺序追加」前提,而并发 append / git 并集合并 / 分片归并三种**正常协作**都会打断它。
|
|
160
162
|
// 缺键(v1-v7)→ 走旧物理链口径,存量零变化。
|
|
161
|
-
// shard(2026-10-12 ledger-shard-write
|
|
162
|
-
//
|
|
163
|
-
// `.agents/confirmations.d/<doc基名>.jsonl`,各 worktree
|
|
164
|
-
//
|
|
163
|
+
// shard(2026-10-12 ledger-shard-write;2026-10-13 ledger-shard-default 升为默认):多 worktree 并行时
|
|
164
|
+
// 两个进程同时 append 同一台账文件会互相覆盖(各自读到同一末行 → 同 prevHash → 后写者覆盖前写者)。
|
|
165
|
+
// 分片把本行写入 `.agents/confirmations.d/<doc基名>.jsonl`,各 worktree 各写各的、互不冲突。
|
|
166
|
+
// **默认写分片**(CLI 层;--no-shard / CONFIRM_SHARDED=0 显式回退主台账)——doc 链未激活
|
|
167
|
+
// (policyVersion < 8)时 CLI 层自动回退主台账(见 isMain 内 ledger-shard-default 注释),
|
|
168
|
+
// 存量装户升级后行为零变化。函数层契约不变:shard 显式传入,默认 false。
|
|
165
169
|
export function appendLedger(root, entry, { shard = false } = {}) {
|
|
166
170
|
const p = shard && entry.doc
|
|
167
171
|
? path.join(root, '.agents', 'confirmations.d', `${path.basename(entry.doc, '.md')}.jsonl`)
|
|
@@ -208,8 +212,8 @@ if (isMain) {
|
|
|
208
212
|
let delegatedQuote = null; // null = TTY 模式;字符串 = 委托代录的用户对话原话
|
|
209
213
|
let toTarget = null; // null = 默认前向跳转;superseded|cancelled = 放弃态显式目标
|
|
210
214
|
let autoMode = false; // AI 自治模式(信任等级授权,2026-09-30 ai-autonomy-trust)
|
|
211
|
-
let batchMode = false; // 协作道批量代录(--batch,2026-09-30 hybrid-governance-risk-lanes:仅 --delegated +
|
|
212
|
-
let shardWrite =
|
|
215
|
+
let batchMode = false; // 协作道批量代录(--batch,2026-09-30 hybrid-governance-risk-lanes:仅 --delegated + L1)
|
|
216
|
+
let shardWrite = process.env.CONFIRM_SHARDED !== '0'; // 分片默认写入口径(2026-10-13 ledger-shard-default):CONFIRM_SHARDED=0 显式回退主台账
|
|
213
217
|
const docs = [];
|
|
214
218
|
for (let i = 0; i < argv.length; i++) {
|
|
215
219
|
if (argv[i] === '--root') root = path.resolve(argv[++i]);
|
|
@@ -218,19 +222,34 @@ if (isMain) {
|
|
|
218
222
|
else if (argv[i] === '--auto') autoMode = true;
|
|
219
223
|
else if (argv[i] === '--batch') batchMode = true;
|
|
220
224
|
else if (argv[i] === '--shard') shardWrite = true;
|
|
225
|
+
else if (argv[i] === '--no-shard') shardWrite = false;
|
|
221
226
|
else docs.push(argv[i].replace(/\\/g, '/'));
|
|
222
227
|
}
|
|
223
228
|
if (toTarget !== null && !ABANDON_TARGETS.includes(toTarget)) {
|
|
224
229
|
console.error(`用法:--to 仅接受 superseded|cancelled(现「${toTarget}」)——cancelled 自 draft/approved/open/fixed;superseded 自 approved/done/fixed/closed(未确认态只能 cancelled;终态间仅允许 done/closed→superseded)`);
|
|
225
230
|
process.exit(1);
|
|
226
231
|
}
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
// 分片默认写入口径(2026-10-13 ledger-shard-default,对齐 Shipyard 参考实现):
|
|
233
|
+
// 确认行默认写 `.agents/confirmations.d/<doc基名>.jsonl`——多 worktree / 多人并行天然各写各的,
|
|
234
|
+
// 互不覆盖;跨分支合并走 .gitattributes 的 union driver(confirmations.d/*.jsonl merge=jsonl-union),
|
|
235
|
+
// 里程碑可选跑 compact-ledgers.mjs 收敛碎片。--no-shard / CONFIRM_SHARDED=0 显式回退主台账。
|
|
236
|
+
// doc 链仍是分片的前提(2026-10-12 ledger-shard-write 口径保留):分片与主台账分属不同文件,
|
|
237
|
+
// 只有 doc 链能跨来源接续;旧物理链口径下分片行的 prevHash 会与主台账末行失配 → 提交即断链。故:
|
|
238
|
+
// 显式 --shard + doc 链未激活 → 参数期直接拒(友好报错,原口径不变);
|
|
239
|
+
// 默认分片 + doc 链未激活 → 自动回退主台账并出 ℹ️——存量装户(policyVersion < 8)升级模板后
|
|
240
|
+
// 行为零变化,分片默认仅对 doc 链已激活的装户生效;升级法:.agents/kit.json 设 policyVersion: 8。
|
|
241
|
+
if (argv.includes('--shard') && argv.includes('--no-shard')) {
|
|
242
|
+
console.error('用法:--shard 与 --no-shard 互斥——分片已是默认写入口径,显式回退主台账只需 --no-shard');
|
|
232
243
|
process.exit(1);
|
|
233
244
|
}
|
|
245
|
+
if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
246
|
+
if (argv.includes('--shard')) {
|
|
247
|
+
console.error('❌ --shard 需要台账 doc 链口径(policy.ledgerDocChainSince),当前 policyVersion 无此键。\n 原因:分片与主台账是不同文件,只有 doc 链能跨来源接续;旧物理链口径下分片行会与主台账断链。\n 修法:在 .agents/kit.json 设 policyVersion: 8(或更高的支持版本)后重试,或改用 --no-shard 显式写主台账。');
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
shardWrite = false; // 存量装户回退:分片默认仅对 doc 链已激活(policyVersion ≥ 8)的装户生效
|
|
251
|
+
console.error('ℹ️ 分片默认写入口径需 doc 链口径(policy.ledgerDocChainSince),当前 policyVersion 未激活——本次写主台账 .agents/confirmations.jsonl。升级法:.agents/kit.json 设 policyVersion: 8。');
|
|
252
|
+
}
|
|
234
253
|
|
|
235
254
|
const delegated = delegatedQuote !== null;
|
|
236
255
|
if (delegated && autoMode) {
|
|
@@ -247,7 +266,7 @@ if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
|
247
266
|
}
|
|
248
267
|
// AI 自治门(2026-09-30 ai-autonomy-trust;2026-09-30 hybrid-governance-explore-hardening 加隐式形态):
|
|
249
268
|
// 读 .agents/trust-mode.json。除显式 --auto 外,Trusted(level=2)下的非 TTY 单份调用在 TTY 门处
|
|
250
|
-
// 免旗标自动转入本分支(见「Trusted 自动泳道」)——
|
|
269
|
+
// 免旗标自动转入本分支(见「Trusted 自动泳道」)——L1 门 / incidents 排除 / 逐阶段与 done 门照常执行。
|
|
251
270
|
let trustConfig = null;
|
|
252
271
|
if (autoMode) {
|
|
253
272
|
try {
|
|
@@ -265,7 +284,7 @@ if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
|
265
284
|
console.error('❌ AI 自治模式一次仅接受一份文档(与 --delegated 同口径,逐件放行)');
|
|
266
285
|
process.exit(1);
|
|
267
286
|
}
|
|
268
|
-
console.error(`⚠️ AI 自治模式(Trust L${trustConfig.level}):仅限
|
|
287
|
+
console.error(`⚠️ AI 自治模式(Trust L${trustConfig.level}):仅限 L1 文档;Level 1 只放行 draft→approved,Level 2 放行至 done`);
|
|
269
288
|
}
|
|
270
289
|
if (delegated && !String(delegatedQuote).trim()) {
|
|
271
290
|
console.error('用法:--delegated 须带用户对话原话(node .agents/scripts/confirm-doc.mjs <doc...> --delegated "<用户原话>")——原话入台账供事后对质,不可缺省');
|
|
@@ -286,7 +305,7 @@ if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
|
286
305
|
if (!delegated && !autoMode && !ttyForced && (!process.stdin.isTTY || !process.stdout.isTTY)) {
|
|
287
306
|
// Trusted 自动泳道(2026-09-30 hybrid-governance-explore-hardening):trust-mode.json 为 Trusted
|
|
288
307
|
// (enabled 且 level=2)时,非 TTY 调用(AI 会话)对文档**免手工触发**(无需 --delegated 原话 /
|
|
289
|
-
// --auto 旗标)自动转入 AI 自治放行——单份、非放弃态才可隐式(与 --auto 同口径,
|
|
308
|
+
// --auto 旗标)自动转入 AI 自治放行——单份、非放弃态才可隐式(与 --auto 同口径,L1 门与
|
|
290
309
|
// incidents 排除由主循环自治门照常执行);不满足则维持原拒绝(fail-closed,Standard/Strict 不隐式)。
|
|
291
310
|
let implicitTrust = null;
|
|
292
311
|
try {
|
|
@@ -294,8 +313,8 @@ if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
|
294
313
|
} catch { implicitTrust = { enabled: false, level: 0 }; }
|
|
295
314
|
if (implicitTrust.enabled && implicitTrust.level === 2 && docs.length === 1 && !toTarget) {
|
|
296
315
|
autoMode = true;
|
|
297
|
-
trustConfig = implicitTrust; // 主循环自治门(
|
|
298
|
-
console.error('⚠️ Trusted 自动泳道(隐式):单份
|
|
316
|
+
trustConfig = implicitTrust; // 主循环自治门(L1 与 incidents 排除)直读本配置——顶部 if (autoMode) 块先于 TTY 门执行,须在此补装
|
|
317
|
+
console.error('⚠️ Trusted 自动泳道(隐式):单份 L1 文档按 AI 自治放行(Level 2 全闭环,台账 source=ai-auto-trust-L2);L2/L3 与 incidents 仍须人工确认');
|
|
299
318
|
} else {
|
|
300
319
|
console.error('确认门须由用户在终端亲手运行(node .agents/scripts/confirm-doc.mjs <workflow/文档>...)——AI 会话内不可代确认;用户对话内明确确认后可用 --delegated "<用户原话>" 委托代录(台账如实记来源)');
|
|
301
320
|
process.exit(1);
|
|
@@ -311,14 +330,14 @@ if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
|
311
330
|
// 单文档默认(2026-09-27 confirm-gate-one-per-call):delegated 没有TTY「逐份过目」的天然机制——
|
|
312
331
|
// 多文档并录曾系统性塌掉三道阶段门(build.md「逐件确认不得并作一次」)。逐件调用:一次一份、
|
|
313
332
|
// 每次带当次用户原话。TTY 形态不受限(用户亲手逐份过目键入,天然逐件)。
|
|
314
|
-
// 协作道批量(2026-09-30 hybrid-governance-risk-lanes):
|
|
333
|
+
// 协作道批量(2026-09-30 hybrid-governance-risk-lanes):L1 可 `--batch` 多份一次代录——
|
|
315
334
|
// 逐份读「级别」,L2/L3 逐份拒绝(见主循环协作道门);台账行带 brief:true,检查 15 据此豁免并录告警。
|
|
316
335
|
if (docs.length > 1) {
|
|
317
336
|
if (!batchMode) {
|
|
318
|
-
console.error(`❌ --delegated 一次仅接受一份文档(现 ${docs.length} 份:${docs.join(' ')})——逐件确认口径(build.md):每份一次调用、每次带当次用户原话;
|
|
337
|
+
console.error(`❌ --delegated 一次仅接受一份文档(现 ${docs.length} 份:${docs.join(' ')})——逐件确认口径(build.md):每份一次调用、每次带当次用户原话;L1 协作道批量可加 --batch`);
|
|
319
338
|
process.exit(1);
|
|
320
339
|
}
|
|
321
|
-
console.error(` 批量代录(--batch):${docs.length} 份,仅受理
|
|
340
|
+
console.error(` 批量代录(--batch):${docs.length} 份,仅受理 L1(协作道,异步审计兜底);L2/L3 逐份拒绝`);
|
|
322
341
|
}
|
|
323
342
|
}
|
|
324
343
|
// 调用事实(2026-09-28 batch-ledger-audit):本次进程调用的批次标识与件序——
|
|
@@ -434,7 +453,7 @@ if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
|
434
453
|
// TTY 形态同样需要(事后对质问的是「放行时盘面上哪些文件在动」,与谁点的确认无关)。
|
|
435
454
|
const scope = machineScope(root, doc);
|
|
436
455
|
// AI 自治放行分支(2026-09-30 ai-autonomy-trust):
|
|
437
|
-
// ① 仅
|
|
456
|
+
// ① 仅 L1(读 frontmatter「级别」,缺级别按不可自治处理——fail-closed);
|
|
438
457
|
// ② Level 1:仅 draft→approved;Level 2:draft→approved 与 approved→done;
|
|
439
458
|
// ③ incidents 永不自治(open→fixed/fixed→closed 必须人工)。
|
|
440
459
|
if (autoMode) {
|
|
@@ -443,16 +462,18 @@ if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
|
443
462
|
? (target === 'approved' || target === 'done')
|
|
444
463
|
: target === 'approved';
|
|
445
464
|
if (!isLowLevel || !stageAllowed || doc.startsWith('workflow/incidents/')) {
|
|
446
|
-
console.error(`❌ ${doc} 未过 AI 自治门(级别「${lvl || '缺失'}」/ 泳道「${lane}」/ 目标态「${target}」/ 类型):Level ${trustConfig.level} 仅授权泳道 low(intent
|
|
465
|
+
console.error(`❌ ${doc} 未过 AI 自治门(级别「${lvl || '缺失'}」/ 泳道「${lane}」/ 目标态「${target}」/ 类型):Level ${trustConfig.level} 仅授权泳道 low(intent 级别 L1;suspect 须先对齐「级别/risk_level」)的 ${trustConfig.level === 2 ? 'approved/done' : 'approved'} 且不含 incidents`);
|
|
447
466
|
refused++;
|
|
448
467
|
continue;
|
|
449
468
|
}
|
|
450
469
|
fs.writeFileSync(abs, applyTransition(text, target, fp.slice(0, 16)));
|
|
470
|
+
// shard 走第三参 options(不进 entry):appendLedger(root, entry, { shard }) 的契约是 options 形参,
|
|
471
|
+
// 写进 entry 会让旗标混入台账行数据且分片开关失效(2026-10-13 ledger-shard-default 修复的存量缺陷)。
|
|
451
472
|
appendLedger(root, {
|
|
452
|
-
|
|
473
|
+
ts: new Date().toISOString(), doc, stage: target, fingerprint: fp, prev: st,
|
|
453
474
|
source: `ai-auto-trust-L${trustConfig.level}`, quote: `AI 自治放行 (Trust Level ${trustConfig.level})`,
|
|
454
475
|
batch, seq, of: docs.length, scope,
|
|
455
|
-
});
|
|
476
|
+
}, { shard: shardWrite });
|
|
456
477
|
console.log(`✓ ${doc} ${st} → ${target}(指纹 ${fp.slice(0, 16)},AI 自治已记账:source=ai-auto-trust-L${trustConfig.level})`);
|
|
457
478
|
confirmed++;
|
|
458
479
|
seq++;
|
|
@@ -460,27 +481,27 @@ if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
|
460
481
|
}
|
|
461
482
|
if (delegated) {
|
|
462
483
|
// 协作道门(2026-09-30 hybrid-governance-risk-lanes;2026-10-02 caliber-convergence 起单源泳道):
|
|
463
|
-
// --batch 批量仅受理泳道 low(intent
|
|
484
|
+
// --batch 批量仅受理泳道 low(intent 级别 L1;suspect 拒)——high/suspect 逐份拒绝落账;
|
|
464
485
|
// 不带 --batch 的单份代录不限级别(既有口径不变)。
|
|
465
486
|
const low = lane === 'low';
|
|
466
487
|
if (batchMode && !low) {
|
|
467
|
-
console.error(`❌ ${doc} 泳道「${lane}」(级别「${lvl || '缺失'}」)不在协作道——--batch
|
|
488
|
+
console.error(`❌ ${doc} 泳道「${lane}」(级别「${lvl || '缺失'}」)不在协作道——--batch 批量代录仅限级别 L1;suspect(级别/risk_level 缺失、非法或分歧)须先对齐两字段,L2/L3 防御道须逐份确认(本份未落账、未写盘)`);
|
|
468
489
|
refused++;
|
|
469
490
|
continue;
|
|
470
491
|
}
|
|
471
492
|
fs.writeFileSync(abs, applyTransition(text, target, fp.slice(0, 16)));
|
|
472
493
|
appendLedger(root, {
|
|
473
|
-
|
|
494
|
+
ts: new Date().toISOString(), doc, stage: target, fingerprint: fp, prev: st,
|
|
474
495
|
source: 'chat-delegated', quote: String(delegatedQuote),
|
|
475
496
|
// 调用事实(batch-ledger-audit):seq=本次调用内件序(从 1),of=本次调用总份数
|
|
476
497
|
batch, seq, of: docs.length,
|
|
477
498
|
// 改动面事实(A1 干净版):quote 降级为「人类可读摘要」,分辨力改由 scope(机器派生)+ fingerprint
|
|
478
499
|
// (文档内容)承担——「不复用同句」纪律据此退役(复用一句摘要不再削弱任何判据)。
|
|
479
500
|
scope,
|
|
480
|
-
// 简洁审计标记(hybrid-governance-risk-lanes):
|
|
501
|
+
// 简洁审计标记(hybrid-governance-risk-lanes):L1 协作道行带 brief(quote 仍入账供对质),
|
|
481
502
|
// 检查 15 并录审计对全 brief 批次豁免「确认并录」告警
|
|
482
503
|
...(low ? { brief: true } : {}),
|
|
483
|
-
});
|
|
504
|
+
}, { shard: shardWrite });
|
|
484
505
|
console.log(low
|
|
485
506
|
? `✓ ${doc} ${st} → ${target} [${lvl} 协作道/异步审计](指纹 ${fp.slice(0, 16)})`
|
|
486
507
|
: `✓ ${doc} ${st} → ${target}(指纹 ${fp.slice(0, 16)},代录已记账:source=chat-delegated)`);
|
|
@@ -500,15 +521,70 @@ if (shardWrite && !ledgerDocChainActive(loadKitPolicy(root))) {
|
|
|
500
521
|
}
|
|
501
522
|
fs.writeFileSync(abs, applyTransition(text, target, fp.slice(0, 16)));
|
|
502
523
|
appendLedger(root, {
|
|
503
|
-
|
|
524
|
+
ts: new Date().toISOString(), doc, stage: target, fingerprint: fp, prev: st,
|
|
504
525
|
source: 'tty',
|
|
505
526
|
batch, seq, of: docs.length, // 调用事实(形态无关:TTY 多文档天然逐件过目,of>1 不构成违规)
|
|
506
|
-
});
|
|
527
|
+
}, { shard: shardWrite });
|
|
507
528
|
console.log(`✓ ${doc} → ${target}(指纹 ${fp.slice(0, 16)},台账已记)`);
|
|
508
529
|
confirmed++;
|
|
509
530
|
seq++;
|
|
510
531
|
}
|
|
511
532
|
if (rl) rl.close();
|
|
533
|
+
// --- 终态件滚动归档(2026-10-13 archive-on-close;B9 自 Shipyard.Material 回灌,行为边界逐字对齐)---
|
|
534
|
+
// 关单即归档:本进程刚落终态的文档,其同名家族若已全终态且超窗、无活跃引用 → git mv 进
|
|
535
|
+
// workflow/archive/<家族最晚终态月>/{类型}/。动机:archive-sweep 是手工工具、无人跑,活跃目录
|
|
536
|
+
// 无限膨胀(源仓实测 885 篇、733 篇终态零引用——留痕在包源仓 git 历史与 CHANGELOG);挂进确认门
|
|
537
|
+
// 后活跃目录恒定。判定单源 = archive-sweep.analyze(禁第二套口径);归档单位 = 同名家族
|
|
538
|
+
// (check-loop 检查 1 的结构配对是家族双向在场审计,单篇归档会造配对断裂——源仓首批返工实证)。
|
|
539
|
+
// 时序:**台账行已写、状态已落盘之后**才 mv——check-loop 检查 15 的内容绑定按台账 doc 路径读
|
|
540
|
+
// 四目录直下文件,归档后该路径不在 buildDocList() 扫描面内,绑定检查自然跳过(归档件不再受
|
|
541
|
+
// 内容绑定约束,与既有归档批次同口径)。失败一律 fail-open:归档是卫生动作,不得回滚已完成的
|
|
542
|
+
// 确认(**归档失败不阻断关单**——与源仓语义逐字一致)。启用锚(照搬源仓,无 policy 锚):
|
|
543
|
+
// ① 仓库已建立归档约定(workflow/archive/ 存在)——真实装户仓有,临时 fixture 无,避免测试夹具
|
|
544
|
+
// 被静默搬走(也避免在未采纳归档的仓库里凭空造目录);② env 逃生门 CONFIRM_DOC_NO_ARCHIVE。
|
|
545
|
+
const archiveRoot = path.join(root, 'workflow', 'archive');
|
|
546
|
+
if (confirmed > 0 && !process.env.CONFIRM_DOC_NO_ARCHIVE && fs.existsSync(archiveRoot)) {
|
|
547
|
+
try {
|
|
548
|
+
const { analyze, buildDocList, loadLastTouch, parseStatus, TERMINAL, ACTIVE } = await import('./archive-sweep.mjs');
|
|
549
|
+
// 台账终态锚:kit 读侧单源 = readLedgerRows(主台账+分片透明合并;源仓走
|
|
550
|
+
// loadAllConfirmations——「每 doc 终态行最大日期」的 max 判据对去重口径无语义差,
|
|
551
|
+
// 见 archive-sweep.loadLedgerTs 同款注释)。CONFIRM_DOC_TEST_NOW 仅测试注入,生产不设。
|
|
552
|
+
const ledgerTs = {};
|
|
553
|
+
for (const e of readLedgerRows(root)) {
|
|
554
|
+
if (TERMINAL.has(e.stage) && e.doc) {
|
|
555
|
+
const d = (e.ts || '').slice(0, 10);
|
|
556
|
+
if (d && (!ledgerTs[e.doc] || d > ledgerTs[e.doc])) ledgerTs[e.doc] = d;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const allDocs = buildDocList(root);
|
|
560
|
+
// 窗口口径:本次确认的文档**刚**落终态,其台账 ts = 今日 → 21 天窗口必然拦下(窗口的用途是
|
|
561
|
+
// 「防归档仍在编辑的在途件」,而刚过确认门的终态件按定义已定稿,窗口对它冗余)。故对本进程
|
|
562
|
+
// 确认过的家族用 windowDays=-1(判据 `floor((today-tTime)/864e5) <= windowDays` 恒 false = 窗口
|
|
563
|
+
// 关闭;注意 0 不等于关闭——0 表示「同日也算未满窗」);未涉及的家族不在此次归档范围(touched
|
|
564
|
+
// 过滤),历史积压仍走 archive-sweep 手工批次 + 21 天窗口。
|
|
565
|
+
const r = analyze({
|
|
566
|
+
docs: allDocs,
|
|
567
|
+
ledgerTs,
|
|
568
|
+
lastTouch: loadLastTouch(root),
|
|
569
|
+
activeTexts: allDocs.filter((d) => ACTIVE.has(parseStatus(d.text))).map((d) => d.text),
|
|
570
|
+
windowDays: -1,
|
|
571
|
+
});
|
|
572
|
+
// 只归档「本次确认涉及的家族」——不越权清理历史积压(积压走 archive-sweep 手工批次)
|
|
573
|
+
const touched = new Set(docs.map((d) => path.basename(d)));
|
|
574
|
+
const mine = r.candidates.filter((x) => touched.has(x.file));
|
|
575
|
+
if (mine.length) {
|
|
576
|
+
for (const x of mine) {
|
|
577
|
+
const destDir = path.join(root, 'workflow', 'archive', x.month, x.dir);
|
|
578
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
579
|
+
execFileSync('git', ['mv', x.rel, path.join('workflow', 'archive', x.month, x.dir, x.file)], { cwd: root });
|
|
580
|
+
}
|
|
581
|
+
const fams = new Set(mine.map((x) => x.file));
|
|
582
|
+
console.log(`📦 归档 ${mine.length} 篇 / ${fams.size} 族 → workflow/archive/<终态月>/(家族原子;活跃目录不再累积终态件)`);
|
|
583
|
+
}
|
|
584
|
+
} catch (err) {
|
|
585
|
+
console.error(`⚠️ 归档跳过(fail-open,不影响已完成的确认):${err.message}`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
512
588
|
console.log(`\n完成:确认 ${confirmed} 份 / 跳过 ${docs.length - confirmed - refused} 份 / 拒绝 ${refused} 份${delegated ? '(委托代录)' : ''}`);
|
|
513
589
|
process.exit(refused ? 2 : 0);
|
|
514
590
|
}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
// L2/L3 时同名 spec)已确认;未过 → exit 2 + 未落盘未记账(S19-S22)
|
|
8
8
|
// ⑧ done 前置门(2026-09-30 confirm-gate-approved-history):→done 须 git 历史已留 approved 态
|
|
9
9
|
// (先 done 后提交 / 两跳同批提交 → 拒绝);非 git / legacy / 存量豁免声明 → 跳过(S25-S29)
|
|
10
|
+
// ⑨ 关单即归档(2026-10-13 archive-on-close,B9 自源仓回灌):终态家族即时 git mv 进 archive、
|
|
11
|
+
// 未涉及家族不越权清理、无归档约定不启用、CONFIRM_DOC_NO_ARCHIVE 逃生门(S43-S45)
|
|
10
12
|
// 用法:node templates/_agents/scripts/confirm-doc.test.mjs(npm test 随跑)
|
|
11
13
|
import fs from 'node:fs';
|
|
12
14
|
import os from 'node:os';
|
|
@@ -615,17 +617,17 @@ const gitHead = (root) => {
|
|
|
615
617
|
fs.rmSync(root, { recursive: true, force: true });
|
|
616
618
|
}
|
|
617
619
|
|
|
618
|
-
// ---- S30【协作道批量】--batch:delegated 多份
|
|
620
|
+
// ---- S30【协作道批量】--batch:delegated 多份 L1 一次代录 → 全部落态 + 台账 brief:true(2026-09-30 hybrid-governance-risk-lanes)----
|
|
619
621
|
{
|
|
620
622
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'confirm-bt1-'));
|
|
621
623
|
fs.mkdirSync(path.join(root, 'workflow', 'intents'), { recursive: true });
|
|
622
624
|
fs.writeFileSync(path.join(root, 'workflow', 'intents', '2026-09-30-bt1.md'), '---\n状态: draft\n级别: L1\n日期: 2026-09-30\n---\n# I1\n');
|
|
623
|
-
fs.writeFileSync(path.join(root, 'workflow', 'intents', '2026-09-30-bt2.md'), '---\n状态: draft\n级别:
|
|
625
|
+
fs.writeFileSync(path.join(root, 'workflow', 'intents', '2026-09-30-bt2.md'), '---\n状态: draft\n级别: L1\n日期: 2026-09-30\n---\n# I2\n');
|
|
624
626
|
const r = spawnSync(process.execPath, [CLI, 'workflow/intents/2026-09-30-bt1.md', 'workflow/intents/2026-09-30-bt2.md', '--delegated', '批量放行', '--batch'], { cwd: root, encoding: 'utf8' });
|
|
625
627
|
const led = readLedgerOf(root);
|
|
626
628
|
const st1 = fs.readFileSync(path.join(root, 'workflow', 'intents', '2026-09-30-bt1.md'), 'utf8');
|
|
627
629
|
const st2 = fs.readFileSync(path.join(root, 'workflow', 'intents', '2026-09-30-bt2.md'), 'utf8');
|
|
628
|
-
check('S30 --batch:2 份
|
|
630
|
+
check('S30 --batch:2 份 L1 一次代录 → 全部 approved + 台账 brief:true / of=2 / seq 递增',
|
|
629
631
|
r.status === 0 && led.length === 2
|
|
630
632
|
&& led.every((e) => e.brief === true && e.source === 'chat-delegated' && e.of === 2 && e.stage === 'approved')
|
|
631
633
|
&& led[0].seq === 1 && led[1].seq === 2
|
|
@@ -694,7 +696,7 @@ const gitHead = (root) => {
|
|
|
694
696
|
fs.rmSync(root, { recursive: true, force: true });
|
|
695
697
|
}
|
|
696
698
|
|
|
697
|
-
// ---- S33 简洁审计:单份 delegated
|
|
699
|
+
// ---- S33 简洁审计:单份 delegated L1(无 --batch)→ 台账 brief:true + 紧凑输出;L2 份不带 brief ----
|
|
698
700
|
{
|
|
699
701
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'confirm-bt4-'));
|
|
700
702
|
fs.mkdirSync(path.join(root, 'workflow', 'intents'), { recursive: true });
|
|
@@ -880,5 +882,179 @@ const writeTrust = (root, cfg) => {
|
|
|
880
882
|
fs.rmSync(root, { recursive: true, force: true });
|
|
881
883
|
}
|
|
882
884
|
|
|
885
|
+
// ---- S42 分片默认写入口径(2026-10-13 ledger-shard-default):CLI 默认写分片 / 两种回退 / 存量回退 / 显式拒 ----
|
|
886
|
+
{
|
|
887
|
+
const mkFixture = (withV8) => {
|
|
888
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'confirm-shard-'));
|
|
889
|
+
const docP = path.join(root, 'workflow', 'intents');
|
|
890
|
+
fs.mkdirSync(docP, { recursive: true });
|
|
891
|
+
fs.writeFileSync(path.join(docP, '2026-10-13-s.md'), '---\n状态: draft\n级别: L1\n---\n# I\n');
|
|
892
|
+
if (withV8) {
|
|
893
|
+
fs.mkdirSync(path.join(root, '.agents'), { recursive: true });
|
|
894
|
+
fs.writeFileSync(path.join(root, '.agents', 'kit.json'), JSON.stringify({ policyVersion: 8 }), 'utf8');
|
|
895
|
+
}
|
|
896
|
+
return root;
|
|
897
|
+
};
|
|
898
|
+
const shardPathOf = (root) => path.join(root, '.agents', 'confirmations.d', '2026-10-13-s.jsonl');
|
|
899
|
+
const mainPathOf = (root) => path.join(root, '.agents', 'confirmations.jsonl');
|
|
900
|
+
const readRows = (p) => (fs.existsSync(p) ? fs.readFileSync(p, 'utf8').split('\n').filter(Boolean).map((l) => JSON.parse(l)) : []);
|
|
901
|
+
const spawnConfirm = (root, args, env = {}) => spawnSync(process.execPath, [CLI, 'workflow/intents/2026-10-13-s.md', '--delegated', '可以', ...args], { cwd: root, encoding: 'utf8', env: { ...process.env, ...env } });
|
|
902
|
+
|
|
903
|
+
// S42① 默认(无旗标)+ doc 链已激活(policyVersion 8)→ 写分片,主台账零行
|
|
904
|
+
{
|
|
905
|
+
const root = mkFixture(true);
|
|
906
|
+
const r = spawnConfirm(root, []);
|
|
907
|
+
check('S42① 默认写分片:exit 0 + 行落 confirmations.d/<doc基名>.jsonl + 主台账零行',
|
|
908
|
+
r.status === 0 && readRows(shardPathOf(root)).length === 1
|
|
909
|
+
&& readRows(shardPathOf(root))[0].source === 'chat-delegated'
|
|
910
|
+
&& readRows(mainPathOf(root)).length === 0,
|
|
911
|
+
JSON.stringify({ status: r.status, stderr: r.stderr }));
|
|
912
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
913
|
+
}
|
|
914
|
+
// S42② --no-shard → 显式回退主台账
|
|
915
|
+
{
|
|
916
|
+
const root = mkFixture(true);
|
|
917
|
+
const r = spawnConfirm(root, ['--no-shard']);
|
|
918
|
+
check('S42② --no-shard 回退主台账:exit 0 + 行落 confirmations.jsonl + 分片不存在',
|
|
919
|
+
r.status === 0 && readRows(mainPathOf(root)).length === 1 && !fs.existsSync(shardPathOf(root)),
|
|
920
|
+
JSON.stringify({ status: r.status, stderr: r.stderr }));
|
|
921
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
922
|
+
}
|
|
923
|
+
// S42③ CONFIRM_SHARDED=0 → 环境变量回退主台账
|
|
924
|
+
{
|
|
925
|
+
const root = mkFixture(true);
|
|
926
|
+
const r = spawnConfirm(root, [], { CONFIRM_SHARDED: '0' });
|
|
927
|
+
check('S42③ CONFIRM_SHARDED=0 回退主台账:exit 0 + 行落 confirmations.jsonl + 分片不存在',
|
|
928
|
+
r.status === 0 && readRows(mainPathOf(root)).length === 1 && !fs.existsSync(shardPathOf(root)),
|
|
929
|
+
JSON.stringify({ status: r.status, stderr: r.stderr }));
|
|
930
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
931
|
+
}
|
|
932
|
+
// S42④ 存量装户(无 kit.json → policyVersion 1,doc 链未激活)默认 → 自动回退主台账 + ℹ️ 升级指引
|
|
933
|
+
{
|
|
934
|
+
const root = mkFixture(false);
|
|
935
|
+
const r = spawnConfirm(root, []);
|
|
936
|
+
check('S42④ 存量装户默认:自动回退主台账(exit 0 + 行落 confirmations.jsonl)+ stderr 带 policyVersion: 8 升级指引 + 分片不存在',
|
|
937
|
+
r.status === 0 && readRows(mainPathOf(root)).length === 1
|
|
938
|
+
&& /policyVersion: 8/.test(r.stderr) && !fs.existsSync(shardPathOf(root)),
|
|
939
|
+
JSON.stringify({ status: r.status, stderr: r.stderr }));
|
|
940
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
941
|
+
}
|
|
942
|
+
// S42⑤ 存量装户显式 --shard → 参数期直接拒(原口径不变)
|
|
943
|
+
{
|
|
944
|
+
const root = mkFixture(false);
|
|
945
|
+
const r = spawnConfirm(root, ['--shard']);
|
|
946
|
+
check('S42⑤ 显式 --shard 缺 doc 链锚:exit 1 + 报错带修法 + 零落账(文档/台账均未动)',
|
|
947
|
+
r.status === 1 && /--shard 需要台账 doc 链口径/.test(r.stderr)
|
|
948
|
+
&& !fs.existsSync(shardPathOf(root)) && !fs.existsSync(mainPathOf(root)),
|
|
949
|
+
JSON.stringify({ status: r.status, stderr: r.stderr }));
|
|
950
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
951
|
+
}
|
|
952
|
+
// S42⑥ --shard 与 --no-shard 互斥 → 用法错误
|
|
953
|
+
{
|
|
954
|
+
const root = mkFixture(true);
|
|
955
|
+
const r = spawnConfirm(root, ['--shard', '--no-shard']);
|
|
956
|
+
check('S42⑥ --shard 与 --no-shard 互斥:exit 1 + 用法提示 + 零落账',
|
|
957
|
+
r.status === 1 && /互斥/.test(r.stderr) && !fs.existsSync(shardPathOf(root)) && !fs.existsSync(mainPathOf(root)),
|
|
958
|
+
JSON.stringify({ status: r.status, stderr: r.stderr }));
|
|
959
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// ---- S43 关单即归档(2026-10-13 archive-on-close;B9 自源仓回灌随件测试):终态家族超窗 → git mv 进 archive ----
|
|
964
|
+
// 需真实 git 仓库(归档走 git mv);家族原子:intent+plan 同进同退。月份断言锚定「实时今月」——
|
|
965
|
+
// 与源仓同口径(台账终态锚 = 实时 now,归档月 = 家族最晚终态月),跨月运行需随今月更新断言。
|
|
966
|
+
{
|
|
967
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'confirm-arch-'));
|
|
968
|
+
const git = (...a) => spawnSync(process.platform === 'win32' ? 'git.exe' : 'git', a, { cwd: root, encoding: 'utf8' });
|
|
969
|
+
git('init', '-q');
|
|
970
|
+
git('config', 'user.email', 't@t');
|
|
971
|
+
git('config', 'user.name', 't');
|
|
972
|
+
const ip = path.join(root, 'workflow', 'intents');
|
|
973
|
+
const pp = path.join(root, 'workflow', 'plans');
|
|
974
|
+
fs.mkdirSync(ip, { recursive: true });
|
|
975
|
+
fs.mkdirSync(pp, { recursive: true });
|
|
976
|
+
fs.mkdirSync(path.join(root, 'workflow', 'archive'), { recursive: true }); // 归档约定在场(启用条件)
|
|
977
|
+
// 家族:intent(本次关单)+ plan(已 done 且正文变更在 30 天前 → 超窗)
|
|
978
|
+
fs.writeFileSync(path.join(ip, '2026-09-01-fam.md'), '---\n状态: approved\n级别: L1\n确认指纹: aaaa1111aaaa1111\n---\n# I\n\n## 验收标准(可测试)\n- [x] 通过(证据:测试全绿)\n', 'utf8');
|
|
979
|
+
fs.writeFileSync(path.join(pp, '2026-09-01-fam.md'), '---\n状态: done\n级别: L1\n---\n# P\n\n## 改动方案\n- 正文\n', 'utf8');
|
|
980
|
+
git('add', '-A');
|
|
981
|
+
spawnSync(process.platform === 'win32' ? 'git.exe' : 'git', ['commit', '-q', '-m', 'c1'], { cwd: root, env: { ...process.env, GIT_AUTHOR_DATE: '2026-09-01T10:00:00+08:00', GIT_COMMITTER_DATE: '2026-09-01T10:00:00+08:00' } });
|
|
982
|
+
const r = spawnSync(process.execPath, [CLI, 'workflow/intents/2026-09-01-fam.md', '--delegated', '可以'],
|
|
983
|
+
{ cwd: root, encoding: 'utf8', env: { ...process.env, CONFIRM_DOC_TEST_NOW: '2026-10-13' } });
|
|
984
|
+
const intentArchived = fs.existsSync(path.join(root, 'workflow', 'archive', '2026-10', 'intents', '2026-09-01-fam.md'));
|
|
985
|
+
const planArchived = fs.existsSync(path.join(root, 'workflow', 'archive', '2026-10', 'plans', '2026-09-01-fam.md'));
|
|
986
|
+
check('S43 关单即归档:终态家族整族 git mv 进 archive(intent+plan 同批)',
|
|
987
|
+
r.status === 0 && intentArchived && planArchived && /📦 归档/.test(r.stdout),
|
|
988
|
+
JSON.stringify({ status: r.status, stdout: String(r.stdout).slice(-300), stderr: String(r.stderr).slice(-200), intentArchived, planArchived }));
|
|
989
|
+
check('S43b 归档后活跃目录不再留该家族', !fs.existsSync(path.join(ip, '2026-09-01-fam.md')) && !fs.existsSync(path.join(pp, '2026-09-01-fam.md')));
|
|
990
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// ---- S44 归档边界:本次确认的家族即时归档(窗口对其冗余);未涉及的家族不越权清理 ----
|
|
994
|
+
{
|
|
995
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'confirm-arch2-'));
|
|
996
|
+
const git = (...a) => spawnSync(process.platform === 'win32' ? 'git.exe' : 'git', a, { cwd: root, encoding: 'utf8' });
|
|
997
|
+
git('init', '-q');
|
|
998
|
+
git('config', 'user.email', 't@t');
|
|
999
|
+
git('config', 'user.name', 't');
|
|
1000
|
+
const ip = path.join(root, 'workflow', 'intents');
|
|
1001
|
+
fs.mkdirSync(ip, { recursive: true });
|
|
1002
|
+
fs.mkdirSync(path.join(root, 'workflow', 'archive'), { recursive: true }); // 归档约定在场(启用条件)
|
|
1003
|
+
// 本次确认的家族(刚落终态 → 即时归档,窗口不适用)
|
|
1004
|
+
fs.writeFileSync(path.join(ip, '2026-10-12-fresh.md'), '---\n状态: approved\n级别: L1\n确认指纹: bbbb2222bbbb2222\n---\n# I\n\n## 验收标准(可测试)\n- [x] 通过(证据:测试全绿)\n', 'utf8');
|
|
1005
|
+
// 未涉及的家族(同为终态、超窗、零引用——但不在本次确认范围,须留守)
|
|
1006
|
+
fs.writeFileSync(path.join(ip, '2026-09-01-other.md'), '---\n状态: done\n级别: L1\n---\n# I2\n\n## 验收标准(可测试)\n- [x] 通过(证据:测试全绿)\n', 'utf8');
|
|
1007
|
+
git('add', '-A');
|
|
1008
|
+
spawnSync(process.platform === 'win32' ? 'git.exe' : 'git', ['commit', '-q', '-m', 'c1'], { cwd: root, env: { ...process.env, GIT_AUTHOR_DATE: '2026-10-12T10:00:00+08:00', GIT_COMMITTER_DATE: '2026-10-12T10:00:00+08:00' } });
|
|
1009
|
+
const r = spawnSync(process.execPath, [CLI, 'workflow/intents/2026-10-12-fresh.md', '--delegated', '可以'],
|
|
1010
|
+
{ cwd: root, encoding: 'utf8', env: { ...process.env, CONFIRM_DOC_TEST_NOW: '2026-10-13' } });
|
|
1011
|
+
check('S44 本次确认家族即时归档(窗口对刚过确认门的终态件冗余)',
|
|
1012
|
+
r.status === 0 && fs.existsSync(path.join(root, 'workflow', 'archive', '2026-10', 'intents', '2026-10-12-fresh.md')) && /📦 归档/.test(r.stdout),
|
|
1013
|
+
JSON.stringify({ status: r.status, stdout: String(r.stdout).slice(-200), stderr: String(r.stderr).slice(-200) }));
|
|
1014
|
+
check('S44b 未涉及的家族不越权清理(历史积压走 archive-sweep 手工批次)',
|
|
1015
|
+
fs.existsSync(path.join(ip, '2026-09-01-other.md')),
|
|
1016
|
+
JSON.stringify({ exists: fs.existsSync(path.join(ip, '2026-09-01-other.md')) }));
|
|
1017
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// ---- S45 归档约定缺席 → 不启用(未采纳归档的仓库不凭空造目录);env 逃生门 CONFIRM_DOC_NO_ARCHIVE ----
|
|
1021
|
+
{
|
|
1022
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'confirm-arch3-'));
|
|
1023
|
+
const git = (...a) => spawnSync(process.platform === 'win32' ? 'git.exe' : 'git', a, { cwd: root, encoding: 'utf8' });
|
|
1024
|
+
git('init', '-q');
|
|
1025
|
+
git('config', 'user.email', 't@t');
|
|
1026
|
+
git('config', 'user.name', 't');
|
|
1027
|
+
const ip = path.join(root, 'workflow', 'intents');
|
|
1028
|
+
fs.mkdirSync(ip, { recursive: true });
|
|
1029
|
+
fs.writeFileSync(path.join(ip, '2026-09-01-noconv.md'), '---\n状态: approved\n级别: L1\n确认指纹: cccc3333cccc3333\n---\n# I\n\n## 验收标准(可测试)\n- [x] 通过(证据:测试全绿)\n', 'utf8');
|
|
1030
|
+
git('add', '-A');
|
|
1031
|
+
spawnSync(process.platform === 'win32' ? 'git.exe' : 'git', ['commit', '-q', '-m', 'c1'], { cwd: root, env: { ...process.env, GIT_AUTHOR_DATE: '2026-09-01T10:00:00+08:00', GIT_COMMITTER_DATE: '2026-09-01T10:00:00+08:00' } });
|
|
1032
|
+
const r = spawnSync(process.execPath, [CLI, 'workflow/intents/2026-09-01-noconv.md', '--delegated', '可以'],
|
|
1033
|
+
{ cwd: root, encoding: 'utf8', env: { ...process.env, CONFIRM_DOC_TEST_NOW: '2026-10-13' } });
|
|
1034
|
+
check('S45 无 workflow/archive/ 约定 → 不归档、不造目录',
|
|
1035
|
+
r.status === 0 && fs.existsSync(path.join(ip, '2026-09-01-noconv.md')) && !fs.existsSync(path.join(root, 'workflow', 'archive')) && !/📦 归档/.test(r.stdout),
|
|
1036
|
+
JSON.stringify({ status: r.status, stdout: String(r.stdout).slice(-200) }));
|
|
1037
|
+
// S45b 有归档约定 + CONFIRM_DOC_NO_ARCHIVE=1 → 逃生门生效(关单不归档)
|
|
1038
|
+
const root2 = fs.mkdtempSync(path.join(os.tmpdir(), 'confirm-arch4-'));
|
|
1039
|
+
const git2 = (...a) => spawnSync(process.platform === 'win32' ? 'git.exe' : 'git', a, { cwd: root2, encoding: 'utf8' });
|
|
1040
|
+
git2('init', '-q');
|
|
1041
|
+
git2('config', 'user.email', 't@t');
|
|
1042
|
+
git2('config', 'user.name', 't');
|
|
1043
|
+
const ip2 = path.join(root2, 'workflow', 'intents');
|
|
1044
|
+
fs.mkdirSync(ip2, { recursive: true });
|
|
1045
|
+
fs.mkdirSync(path.join(root2, 'workflow', 'archive'), { recursive: true });
|
|
1046
|
+
fs.writeFileSync(path.join(ip2, '2026-09-01-optout.md'), '---\n状态: approved\n级别: L1\n确认指纹: dddd4444dddd4444\n---\n# I\n\n## 验收标准(可测试)\n- [x] 通过(证据:测试全绿)\n', 'utf8');
|
|
1047
|
+
git2('add', '-A');
|
|
1048
|
+
spawnSync(process.platform === 'win32' ? 'git.exe' : 'git', ['commit', '-q', '-m', 'c1'], { cwd: root2, env: { ...process.env, GIT_AUTHOR_DATE: '2026-09-01T10:00:00+08:00', GIT_COMMITTER_DATE: '2026-09-01T10:00:00+08:00' } });
|
|
1049
|
+
const r2 = spawnSync(process.execPath, [CLI, 'workflow/intents/2026-09-01-optout.md', '--delegated', '可以'],
|
|
1050
|
+
{ cwd: root2, encoding: 'utf8', env: { ...process.env, CONFIRM_DOC_TEST_NOW: '2026-10-13', CONFIRM_DOC_NO_ARCHIVE: '1' } });
|
|
1051
|
+
check('S45b CONFIRM_DOC_NO_ARCHIVE 逃生门:关单成功且不归档(状态已置 done)',
|
|
1052
|
+
r2.status === 0 && /状态: done/.test(fs.readFileSync(path.join(ip2, '2026-09-01-optout.md'), 'utf8'))
|
|
1053
|
+
&& !fs.existsSync(path.join(root2, 'workflow', 'archive', 'intents')) && !/📦 归档/.test(r2.stdout),
|
|
1054
|
+
JSON.stringify({ status: r2.status, stdout: String(r2.stdout).slice(-200) }));
|
|
1055
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
1056
|
+
fs.rmSync(root2, { recursive: true, force: true });
|
|
1057
|
+
}
|
|
1058
|
+
|
|
883
1059
|
console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
|
|
884
1060
|
process.exit(fail ? 1 : 0);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// 工作树编辑期零反馈(实测:常驻面重构砍掉阶段索引,到提交才被检查 7 报警)。本脚本把判定左移到编辑时。
|
|
5
5
|
// 判定零复刻铁律:闭环判定**单源**复用 check-loop——以 CHECK_LOOP_ROOT=<root>(全扫模式,扫工作树
|
|
6
6
|
// 非 HEAD)子进程消费其裁决,本脚本不实现任何门判定;唯一自算是常驻面字节预算 advisory(工作树
|
|
7
|
-
// LF 字节 vs rule-budgets.txt 上限——提交门口径走 git index/HEAD(rule-budget.
|
|
7
|
+
// LF 字节 vs rule-budgets.txt 上限——提交门口径走 git index/HEAD(rule-budget.mjs),两者判据不同面,
|
|
8
8
|
// 此处为编辑时 advisory,冲突时以提交门为准)。
|
|
9
9
|
// exit 语义:check-loop hard(exit≠0)→ 2;预算 advisory 超限 → 1;其余 → 0。
|
|
10
10
|
// WARN **不进 exit**(打印明细供 AI/人对照)——绝对 WARN 计数会让带存量 advisory 的仓库恒非零
|
|
@@ -53,9 +53,11 @@ export function budgetAdvisory(files, budgetsText) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// runCheck:工作树面 check-loop(CHECK_LOOP_ROOT 全扫)。返回 { code, output, ms }。
|
|
56
|
+
// --all 显式全量(2026-10-13 分道后裸跑=默认硬门):本工具的动机就是编辑期暴露卫生面 WARN
|
|
57
|
+
// (如阶段索引漂移——头部动机段),硬门子集满足不了,须显式声明全量。
|
|
56
58
|
function runCheck(root, scriptDir) {
|
|
57
59
|
const t0 = Date.now();
|
|
58
|
-
const r = spawnSync(process.execPath, [path.join(scriptDir, 'check-loop.mjs')], {
|
|
60
|
+
const r = spawnSync(process.execPath, [path.join(scriptDir, 'check-loop.mjs'), '--all'], {
|
|
59
61
|
cwd: root, encoding: 'utf8', windowsHide: true,
|
|
60
62
|
env: { ...process.env, CHECK_LOOP_ROOT: root },
|
|
61
63
|
});
|
|
@@ -84,7 +86,7 @@ export function main(root = process.cwd(), scriptDir = path.dirname(fileURLToPat
|
|
|
84
86
|
if (/\[WARN /.test(line)) console.log(' ' + line.trim().slice(0, 200));
|
|
85
87
|
}
|
|
86
88
|
for (const { rel, limit, actual } of over) {
|
|
87
|
-
console.log(` ⚠️ 预算 advisory:${rel} 工作树 ${actual}B > 上限 ${limit}B(编辑时口径;提交门以 rule-budget.
|
|
89
|
+
console.log(` ⚠️ 预算 advisory:${rel} 工作树 ${actual}B > 上限 ${limit}B(编辑时口径;提交门以 rule-budget.mjs 为准)`);
|
|
88
90
|
}
|
|
89
91
|
if (ck.code !== 0) {
|
|
90
92
|
console.error('❌ 闭环判定 hard 未过——先修复再提交(判定单源 check-loop,详见上方输出)');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// flow-kit fill-intent:起草 intent 草稿(背景与问题/历史教训防复发/目标/非目标/约束/影响面/触达红线/验收标准 8 节正文 + 确认与复核 共 9 节)
|
|
2
|
-
// 读 workflow/intents/_TEMPLATE.md 的节标题 → 输出符合 frontmatter 受限子集(
|
|
2
|
+
// 读 workflow/intents/_TEMPLATE.md 的节标题 → 输出符合 frontmatter 受限子集(7 字段,含 risk_level 风险泳道与预判路由)的草稿到指定文件
|
|
3
3
|
// 用法:node .agents/scripts/fill-intent.mjs --module pipeline --level L1 --topic "test" --output /tmp/x.md
|
|
4
4
|
// 零依赖;CLI 解析用最简字符串匹配;不替代 AI 起草——本工具只输出模板与提示句
|
|
5
5
|
import fs from 'node:fs';
|
|
@@ -41,10 +41,12 @@ const SECTIONS = [
|
|
|
41
41
|
// renderIntent:骨架渲染(纯函数)。kbHits(2026-10-09 engine-quality-round3 W2):kb-search 命中行
|
|
42
42
|
// 数组(📄 行原文)——非空时替换「历史教训/防复发」节的检索占位行为实际命中列表(历史坑强制注入,
|
|
43
43
|
// 手动路由与 pipeline-run 工单同权);空/缺省保留占位(fail-open 口径)。子进程调用在 main,本函数保持无 IO。
|
|
44
|
+
// 预判(2026-10-13 自 Shipyard §25 prejudgment-ledger 回灌):intents≡require 恒真、level 与「级别」双写
|
|
45
|
+
// 同值——机器可推导字段直出(与 B6b 路由预判一致性检查对账零 WARN);advisory 灰度,缺字段零告警。
|
|
44
46
|
export function renderIntent({ topic, module, level, notes, date = TODAY, kbHits = [], kbFailNote = '' }) {
|
|
45
47
|
if (!LEVELS.includes(level)) throw new Error('level 不在枚举 ' + LEVELS.join('|') + ':' + level);
|
|
46
|
-
const fm = '---\n状态: draft\n级别: ' + level + '\nrisk_level: ' + level + '\n日期: ' + date + '\n模块: ' + module + '\n备注: ' + (notes || '<可选:附注自由文本,check-loop 不解析>') + '\n---\n';
|
|
47
|
-
let body = '# INTENT — ' + topic + '\n\n<!-- 复制本模板为 YYYY-MM-DD-<主题>.md 后填写;plans/ 下同名文件与本文件配对 -->\n<!-- frontmatter 受限子集(2026-09-13):每行 键: 值;状态∈draft/approved/done/superseded/cancelled(严格枚举,附注写备注键);级别∈
|
|
48
|
+
const fm = '---\n状态: draft\n级别: ' + level + '\n预判: kind=require level=' + level + '\nrisk_level: ' + level + '\n日期: ' + date + '\n模块: ' + module + '\n备注: ' + (notes || '<可选:附注自由文本,check-loop 不解析>') + '\n---\n';
|
|
49
|
+
let body = '# INTENT — ' + topic + '\n\n<!-- 复制本模板为 YYYY-MM-DD-<主题>.md 后填写;plans/ 下同名文件与本文件配对 -->\n<!-- frontmatter 受限子集(2026-09-13):每行 键: 值;状态∈draft/approved/done/superseded/cancelled(严格枚举,附注写备注键);级别∈L1/L2/L3(2026-10-13 L0 退役);risk_level=级别(混合治理风险泳道选道:L1 协作道异步审计 / L2/L3 防御道同步确认);check-loop 只扫 frontmatter 取机器字段,正文不再写状态/级别行 -->\n\n';
|
|
48
50
|
for (const { title, hint } of SECTIONS) {
|
|
49
51
|
let section = hint;
|
|
50
52
|
if (title === '历史教训/防复发') {
|