agentic-flow-kit 1.1.1 → 1.1.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-flow-kit",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "AI-Native 闭环工作流 + wiki 知识层脚手架——flow-kit init 一条命令为新项目装好 .agents 工作流引擎(阶段命令/角色契约/门禁钩子/kb 检索/看板/规则面预算)+ workflow 文档闭环 + wiki 知识库,装完自包含、零运行时依赖",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -242,7 +242,7 @@ function gitOut(args, { ok = () => true } = {}) {
|
|
|
242
242
|
// fs 读取同走 fail-loud(2026-10-05-gitout-fail-open 实现期诊断追加,与 spawnGit 同一故障类):
|
|
243
243
|
// readFileSync 瞬时异常(宿主资源压力 EMFILE/共享冲突类)重试一次,仍失败响亮出账(进程级去重)
|
|
244
244
|
// 后按 null 降级——调用方按空文档跳过该件,但异常本身不再静默
|
|
245
|
-
let fsReadWarned =
|
|
245
|
+
let fsReadWarned = new Set();
|
|
246
246
|
const fileLinesCache = new Map();
|
|
247
247
|
function linesOf(file) {
|
|
248
248
|
if (!fileLinesCache.has(file)) {
|
|
@@ -251,8 +251,8 @@ function linesOf(file) {
|
|
|
251
251
|
lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
|
|
252
252
|
} catch (e) {
|
|
253
253
|
try { lines = fs.readFileSync(file, 'utf8').split(/\r?\n/); } catch { lines = null; }
|
|
254
|
-
if (!lines && !fsReadWarned) {
|
|
255
|
-
fsReadWarned
|
|
254
|
+
if (!lines && !fsReadWarned.has(file)) {
|
|
255
|
+
fsReadWarned.add(file);
|
|
256
256
|
console.error(`check-loop: 文档读取异常 ${path.basename(file)}(${e.code || e.message})——按空文档降级,检查面可能收窄`);
|
|
257
257
|
}
|
|
258
258
|
}
|
|
@@ -605,30 +605,54 @@ const addedDateOf = (absDoc) => {
|
|
|
605
605
|
return iso ? iso.slice(0, 10) : '';
|
|
606
606
|
};
|
|
607
607
|
|
|
608
|
+
// 豁免/放行分支出账(fail-loud 口径——「裁决消失」不可归因的教训:豁免路径零痕迹;
|
|
609
|
+
// 2026-10-05-check8-digit-sha-misfire):按 type 进程级去重,每类至多一条;stderr 不改 stdout 协议
|
|
610
|
+
const evidenceExemptWarned = new Set();
|
|
611
|
+
function warnEvidenceExempt(type, base) {
|
|
612
|
+
if (evidenceExemptWarned.has(type)) return;
|
|
613
|
+
evidenceExemptWarned.add(type);
|
|
614
|
+
console.error(`check-loop: 证据豁免 ${type} ${base}——该文档的验收证据未做真相核验`);
|
|
615
|
+
}
|
|
616
|
+
|
|
608
617
|
// 证据真相校验:检查证据字符串是否包含合法的 commit SHA,且该 commit 触及了 plan 声明的文件
|
|
609
618
|
function verifyEvidenceTruth(evidenceStr, planBase, root) {
|
|
610
619
|
const shaMatch = evidenceStr.match(/\b([a-f0-9]{7,40})\b/i);
|
|
611
|
-
|
|
612
|
-
|
|
620
|
+
if (!shaMatch) {
|
|
621
|
+
warnEvidenceExempt('text', planBase);
|
|
622
|
+
return { ok: true, type: 'text' };
|
|
623
|
+
}
|
|
613
624
|
|
|
614
625
|
const sha = shaMatch[1];
|
|
615
626
|
// 直连走 spawnGit(与 gitOut 同一 loud+retry 通道,注释互引):spawn 异常不再被静默算作业务失败;
|
|
616
627
|
// 重试后仍异常按下方 status!==0 判定(fail-closed 保持——本地核验不了就按伪造拦)
|
|
617
628
|
const revParse = spawnGit(['rev-parse', sha]);
|
|
618
629
|
if (revParse.status !== 0) {
|
|
630
|
+
// 纯数字串且解析失败 → 时间戳/ID 维持文本豁免(2026-10-05-check8-digit-sha-misfire:形态守卫
|
|
631
|
+
// 曾把全数字短 SHA≈4.4%/夹具 误分类为文本致核验静默跳过——改由 rev-parse 实证分流)。
|
|
632
|
+
// 纪律:文本启发式须可被实证兜底,禁以字符形态抢先分类。
|
|
633
|
+
if (/^\d+$/.test(sha)) {
|
|
634
|
+
warnEvidenceExempt('text', planBase);
|
|
635
|
+
return { ok: true, type: 'text' };
|
|
636
|
+
}
|
|
619
637
|
// 区分本地伪造与外部仓库引用:
|
|
620
638
|
// 剔除 SHA 与泛指词后仍有其他内容(如项目名、路径描述)→ 视为外部仓库引用,不硬拦(本地无法核验他仓哈希);
|
|
621
639
|
// 剔除后为空(如纯「commit a1b2c3d」「提交 a1b2c3d」)→ 视为本地引用伪造,硬拦。
|
|
622
640
|
const residue = evidenceStr.replace(sha, '')
|
|
623
641
|
.replace(/证据[::]|commit|提交|[()()。—-]/gi, '')
|
|
624
642
|
.trim();
|
|
625
|
-
if (residue.length > 0)
|
|
643
|
+
if (residue.length > 0) {
|
|
644
|
+
warnEvidenceExempt('external', planBase);
|
|
645
|
+
return { ok: true, type: 'external' };
|
|
646
|
+
}
|
|
626
647
|
return { ok: false, type: 'forged', msg: `提交 ${sha} 不存在` };
|
|
627
648
|
}
|
|
628
649
|
|
|
629
|
-
//
|
|
650
|
+
// 解析成功(含纯数字短 SHA 命中提交/ref 的情形)→ 真相核验
|
|
630
651
|
const planPath = path.join(ROOT, WF, 'plans', planBase + '.md');
|
|
631
|
-
if (!fs.existsSync(planPath))
|
|
652
|
+
if (!fs.existsSync(planPath)) {
|
|
653
|
+
warnEvidenceExempt('no-plan', planBase);
|
|
654
|
+
return { ok: true, type: 'no-plan' }; // 缺 plan 走 Check 1 拦截
|
|
655
|
+
}
|
|
632
656
|
|
|
633
657
|
const planLines = linesOf(planPath) || [];
|
|
634
658
|
const declaredFiles = [];
|
|
@@ -655,6 +679,7 @@ function verifyEvidenceTruth(evidenceStr, planBase, root) {
|
|
|
655
679
|
// 与 run 事件流同源,git 历史留痕可对质)。只救 irrelevant:forged(SHA 不存在)在前已判,其余判据不动。
|
|
656
680
|
const subj = spawnGit(['log', '-1', '--format=%s', sha]);
|
|
657
681
|
if (subj.status === 0 && /pipeline-run [0-9]{8}-[0-9]{4}-[A-Za-z0-9-]+/.test(subj.stdout)) {
|
|
682
|
+
warnEvidenceExempt('process', planBase);
|
|
658
683
|
return { ok: true, type: 'process' };
|
|
659
684
|
}
|
|
660
685
|
return { ok: false, type: 'irrelevant', msg: `提交 ${sha} 未触及 plan 声明的任何文件` };
|
|
@@ -754,6 +754,33 @@ const evidenceFixture = (slug, declared, createDeclared) => {
|
|
|
754
754
|
check('git 基础设施异常:正常 git 行为不变(无标记无关提交仍拦证据无关)', r.status === 1 && outOf(r).includes('证据无关'), `exit=${r.status}\n${outOf(r)}`);
|
|
755
755
|
rmfix(T);
|
|
756
756
|
}
|
|
757
|
+
// ---- 场景 5u:检查 8 全数字证据串——rev-parse 实证分流(2026-10-05-check8-digit-sha-misfire)----
|
|
758
|
+
// 纯数字证据串曾由 /^\d+$/ 形态守卫抢先 text 豁免(全数字短 SHA≈4.4%/夹具 静默漏检);改为实证分流:
|
|
759
|
+
// 能解析成提交(含同名 ref/tag)→ 走真相核验;解析失败 → 维持 text 豁免。构造性钉子:git tag 数字名,不依赖哈希运气
|
|
760
|
+
{
|
|
761
|
+
const T = mkfix();
|
|
762
|
+
gitInit(T);
|
|
763
|
+
w(T, 'workflow/plans/2026-10-04-ev5.md', PLAN('ev5', '状态: done\n级别: L1', '\n## 改动方案\n- src/ghost.js:声明文件(实际不存在,证据必不触及)\n'));
|
|
764
|
+
w(T, 'workflow/intents/2026-10-04-ev5.md', INTENT('ev5', '状态: done\n级别: L1\n日期: 2026-10-04', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:2620913)\n'));
|
|
765
|
+
gitCommitAll(T, 'feat: ev5 fixture');
|
|
766
|
+
gitRetry(['tag', '2620913', 'HEAD'], T);
|
|
767
|
+
const r = run(T);
|
|
768
|
+
check('全数字证据串可解析成提交(同名 ref)→ 按真相核验报证据无关(数字形态不豁免)',
|
|
769
|
+
outOf(r).includes('证据无关'), `exit=${r.status}\n${outOf(r)}`);
|
|
770
|
+
rmfix(T);
|
|
771
|
+
}
|
|
772
|
+
{
|
|
773
|
+
const T = mkfix();
|
|
774
|
+
gitInit(T);
|
|
775
|
+
w(T, 'workflow/plans/2026-10-04-ev6.md', PLAN('ev6', '状态: done\n级别: L1', '\n## 改动方案\n- src/ghost.js:声明文件(实际不存在,证据必不触及)\n'));
|
|
776
|
+
w(T, 'workflow/intents/2026-10-04-ev6.md', INTENT('ev6', '状态: done\n级别: L1\n日期: 2026-10-04', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:20260926)\n'));
|
|
777
|
+
gitCommitAll(T, 'feat: ev6 fixture');
|
|
778
|
+
const r = run(T);
|
|
779
|
+
check('纯数字时间戳证据(无同名 ref)→ 维持文本豁免不误报',
|
|
780
|
+
!outOf(r).includes('证据无关') && !outOf(r).includes('证据伪造'), `exit=${r.status}\n${outOf(r)}`);
|
|
781
|
+
rmfix(T);
|
|
782
|
+
}
|
|
783
|
+
|
|
757
784
|
// ---- 场景 45:检查 2——真未填占位(标题 <主题>,无 .md)→ 仍报模板未填 ----
|
|
758
785
|
{
|
|
759
786
|
const T = mkfix();
|