@tea-agent/loop-agent 0.21.0 → 0.23.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.
Files changed (108) hide show
  1. package/AGENTS.md +42 -108
  2. package/CHANGELOG.md +114 -0
  3. package/README.md +7 -4
  4. package/bin/agent-worker.js +0 -0
  5. package/dist/adapters/loop-agent.js +52 -0
  6. package/dist/application/context-usage/skill-resolution-stats.js +263 -0
  7. package/dist/application/dag/generate-task-dag.js +17 -3
  8. package/dist/cli/command-definitions.js +8 -7
  9. package/dist/cli/program.js +17 -15
  10. package/dist/commands/doctor.js +269 -18
  11. package/dist/commands/init.js +198 -86
  12. package/dist/commands/stats.js +40 -11
  13. package/dist/executors/dag-pi-executor.js +2 -0
  14. package/dist/executors/shell-executor.js +162 -19
  15. package/dist/shared/openspec-spec.js +49 -0
  16. package/dist/shared/operator/capabilities.js +11 -1
  17. package/dist/worker/console/app-data.js +4 -0
  18. package/dist/worker/console/chat/instruction-skills.js +217 -0
  19. package/dist/worker/console/chat/model-resolver.js +106 -0
  20. package/dist/worker/console/chat/pi-runtime.js +605 -0
  21. package/dist/worker/console/chat/resource-loader.js +66 -0
  22. package/dist/worker/console/chat/routes.js +357 -0
  23. package/dist/worker/console/chat/session-store.js +238 -0
  24. package/dist/worker/console/chat/tool-adapter.js +238 -0
  25. package/dist/worker/console/chat/tools.js +171 -0
  26. package/dist/worker/console/server.js +55 -0
  27. package/dist/worker/console/static/assets/index-DVl7Jxt5.js +25 -0
  28. package/dist/worker/console/static/assets/index-lVcIr9Ju.css +1 -0
  29. package/dist/worker/console/static/index.html +2 -2
  30. package/dist/worker/feature/profile-schema.js +1 -1
  31. package/dist/worker/observability/read-model.js +21 -1
  32. package/dist/worker/observe/spec-evidence.js +12 -15
  33. package/dist/worker/observe/static/dag-helpers.js +22 -0
  34. package/dist/worker/observe/static/views/dag.js +5 -0
  35. package/dist/workflows/dag/backend-test-markdown-workflow.js +37 -0
  36. package/dist/workflows/dag/frontend-implementation-contract.js +141 -32
  37. package/dist/workflows/dag/frontend-lint-baseline.js +471 -0
  38. package/dist/workflows/dag/frontend-prewrite-gate.js +79 -16
  39. package/dist/workflows/dag/frontend-project-capability.js +11 -8
  40. package/dist/workflows/dag/frontend-repair.js +6 -4
  41. package/dist/workflows/dag/frontend-review-context.js +67 -0
  42. package/dist/workflows/dag/frontend-test-case-quality.js +105 -0
  43. package/dist/workflows/dag/frontend-test-result-contract.js +71 -66
  44. package/dist/workflows/dag/frontend-verification-trace.js +31 -1
  45. package/dist/workflows/dag/frontend-worktree-diff.js +81 -6
  46. package/dist/workflows/dag/init-hybrid.js +412 -84
  47. package/dist/workflows/dag/node-execution.js +38 -1
  48. package/dist/workflows/dag/output-protocol.js +89 -0
  49. package/dist/workflows/dag/prompt.js +35 -1
  50. package/dist/workflows/dag/recovery-recommendation.js +45 -0
  51. package/dist/workflows/dag/report.js +28 -1
  52. package/dist/workflows/dag/rerun-task.js +1 -1
  53. package/dist/workflows/dag/scheduler.js +9 -0
  54. package/dist/workflows/dag/types.js +74 -1
  55. package/dist/workflows/dag/validate.js +55 -0
  56. package/docs/README.md +73 -156
  57. package/docs/architecture/README.md +3 -2
  58. package/docs/architecture/dag-execution.md +2 -2
  59. package/docs/architecture/evolution.md +14 -12
  60. package/docs/architecture/system-overview.md +1 -1
  61. package/docs/architecture/worker-and-feature.md +3 -3
  62. package/docs/governance/README.md +15 -0
  63. package/docs/{harness-methodology-debugging.md → governance/harness-methodology-debugging.md} +27 -3
  64. package/docs/init-surface.manifest.json +22 -4
  65. package/docs/operations/README.md +12 -0
  66. package/docs/{local-development-environment.md → operations/local-development-environment.md} +1 -1
  67. package/docs/skills/vetted-skill-registry.md +23 -3
  68. package/docs/templates/README.md +55 -0
  69. package/docs/templates/agent-dag.schema.json +15 -5
  70. package/docs/templates/backend-test-dag.json +1 -1
  71. package/docs/templates/evaluation/agents-map-slim-v1.candidate.json +9 -0
  72. package/docs/templates/evaluation/agents-map-slim-v1.md +87 -0
  73. package/docs/templates/evaluation/agents-map-verbose-v0.candidate.json +9 -0
  74. package/docs/templates/evaluation/agents-map-verbose-v0.md +153 -0
  75. package/docs/templates/frontend-implementation-contract.schema.json +4 -3
  76. package/docs/templates/frontend-test-case-checklist.md +6 -2
  77. package/docs/templates/frontend-test-dag.json +2 -2
  78. package/docs/templates/hybrid-dag.json +1 -1
  79. package/docs/templates/progress-log.md +9 -2
  80. package/harness.json +5 -5
  81. package/package.json +5 -5
  82. package/scripts/kb-bootstrap-init-skeleton.sh +2 -2
  83. package/skills/agent-worker/SKILL.md +1 -1
  84. package/skills/frontend-design-review/SKILL.md +12 -10
  85. package/skills/frontend-design-review/references/review-checklist.md +4 -4
  86. package/skills/frontend-implementation/SKILL.md +2 -2
  87. package/skills/frontend-implementation/references/code-standards.md +4 -3
  88. package/skills/frontend-implementation/references/design-spec.md +19 -14
  89. package/skills/frontend-implementation/references/node-contracts.md +2 -2
  90. package/skills/frontend-review/SKILL.md +15 -28
  91. package/skills/frontend-review/references/review-findings.md +16 -18
  92. package/skills/frontend-verification/SKILL.md +16 -13
  93. package/skills/frontend-verification/references/verification-checklist.md +18 -30
  94. package/skills/grill-with-docs/SKILL.md +44 -52
  95. package/skills/grill-with-docs/adr-format.md +37 -26
  96. package/skills/grill-with-docs/context-format.md +18 -26
  97. package/skills/loop-agent/SKILL.md +28 -112
  98. package/skills/loop-agent/references/command-reference.md +9 -3
  99. package/skills/loop-agent/references/harness-policy.md +3 -3
  100. package/skills/loop-agent/references/hybrid-dag.md +1 -1
  101. package/skills/loop-agent/references/orchestrator-and-interventions.md +1 -1
  102. package/skills/loop-agent/references/task-workflow.md +2 -0
  103. package/skills/systematic-debugging/SKILL.md +20 -4
  104. package/skills/test-driven-development/SKILL.md +10 -3
  105. package/dist/worker/console/static/assets/index-CUDke82y.js +0 -18
  106. package/dist/worker/console/static/assets/index-wSEksVSO.css +0 -1
  107. /package/docs/{harness-methodology-tdd.md → governance/harness-methodology-tdd.md} +0 -0
  108. /package/docs/{harness-methodology-verification.md → governance/harness-methodology-verification.md} +0 -0
@@ -13,16 +13,20 @@ const GITIGNORE_BLOCK_END = "# LOOP_AGENT_INIT_END";
13
13
  const INIT_SURFACE_STATE_PATH = ".harness/init-surface.json";
14
14
  const DEFAULT_GOVERNANCE_ROOT = "ai_workspace/loop-agent";
15
15
  const CORE_DOC_FILES = [
16
- "README.md",
17
- "architecture/runtime-boundaries.md",
18
- "development-principles.md",
19
- "feature-workflow.md",
20
- "verification-matrix.md",
21
- "loop-agent-harness.md",
22
- "harness-methodology-tdd.md",
23
- "harness-methodology-verification.md",
24
- "harness-methodology-debugging.md",
16
+ { source: "README.md", target: "README.md" },
17
+ { source: "architecture/runtime-boundaries.md", target: "architecture/runtime-boundaries.md" },
18
+ { source: "governance/development-principles.md", target: "development-principles.md" },
19
+ { source: "governance/feature-workflow.md", target: "feature-workflow.md" },
20
+ { source: "governance/verification-matrix.md", target: "verification-matrix.md" },
21
+ { source: "runtime/loop-agent-harness.md", target: "loop-agent-harness.md" },
22
+ { source: "governance/harness-methodology-tdd.md", target: "harness-methodology-tdd.md" },
23
+ { source: "governance/harness-methodology-verification.md", target: "harness-methodology-verification.md" },
24
+ { source: "governance/harness-methodology-debugging.md", target: "harness-methodology-debugging.md" },
25
+ { source: "templates/README.md", target: "templates/README.md" },
25
26
  ];
27
+ function coreDocSourceForTarget(target) {
28
+ return CORE_DOC_FILES.find((entry) => entry.target === target)?.source ?? target;
29
+ }
26
30
  const GOVERNANCE_README_DIRS = [
27
31
  "decisions",
28
32
  "design",
@@ -522,10 +526,107 @@ has_npm_script() {
522
526
  node -e "const fs=require('fs'); const p=JSON.parse(fs.readFileSync('package.json','utf8')); process.exit(p.scripts && p.scripts[process.argv[1]] ? 0 : 1)" "\${script}" >/dev/null 2>&1
523
527
  }
524
528
 
529
+ dag_lint_assessment_allows_skip() {
530
+ [[ -n "\${HARNESS_DAG_RUN_DIR:-}" ]] || return 1
531
+ command -v node >/dev/null 2>&1 || return 1
532
+ node - "\${HARNESS_DAG_RUN_DIR}" <<'NODE'
533
+ const fs = require("fs");
534
+ const path = require("path");
535
+ const crypto = require("crypto");
536
+ const runDir = path.resolve(process.argv[2]);
537
+ const assessmentPath = path.join(runDir, "contracts", "frontend-lint-assessment.json");
538
+ const fail = () => process.exit(1);
539
+ const sha256 = (value) => crypto.createHash("sha256").update(value).digest("hex");
540
+ const readContained = (relative) => {
541
+ if (typeof relative !== "string" || path.isAbsolute(relative)) fail();
542
+ const absolute = path.resolve(runDir, relative);
543
+ if (absolute !== runDir && !absolute.startsWith(runDir + path.sep)) fail();
544
+ return fs.readFileSync(absolute);
545
+ };
546
+ let assessment;
547
+ try {
548
+ assessment = JSON.parse(fs.readFileSync(assessmentPath, "utf8"));
549
+ } catch {
550
+ fail();
551
+ }
552
+ if (
553
+ assessment.schemaVersion !== 1 ||
554
+ assessment.schemaId !== "frontend-lint-assessment-v1" ||
555
+ !["passed", "baseline-debt"].includes(assessment.status) ||
556
+ !assessment.commandIdentity ||
557
+ !Array.isArray(assessment.commandIdentity.commands) ||
558
+ assessment.commandIdentity.commands.length === 0 ||
559
+ !Array.isArray(assessment.writerChangedFiles) ||
560
+ !Array.isArray(assessment.blockingDiagnostics) ||
561
+ assessment.blockingDiagnostics.length !== 0 ||
562
+ !Array.isArray(assessment.blockingReasons) ||
563
+ assessment.blockingReasons.length !== 0
564
+ ) fail();
565
+ const commandHash = sha256(JSON.stringify(assessment.commandIdentity.commands));
566
+ if (commandHash !== assessment.commandIdentity.sha256) fail();
567
+ if (!assessment.commandIdentity.commands.every((command) =>
568
+ /(?:^|[\\s'"])npm(?:['"])?\\s+(?:['"])?run(?:['"])?\\s+(?:['"])?lint(?:['"])?(?:\\s|$)/.test(command)
569
+ )) fail();
570
+ if (
571
+ (assessment.status === "passed" && assessment.currentExitCode !== 0) ||
572
+ (assessment.status === "baseline-debt" && assessment.currentExitCode === 0)
573
+ ) fail();
574
+ if (!assessment.baselineRef || assessment.baselineRef.nodeId !== "frontend-lint-baseline-shell") fail();
575
+ const baselineRaw = readContained(assessment.baselineRef.path);
576
+ if (sha256(baselineRaw) !== assessment.baselineRef.sha256) fail();
577
+ let baseline;
578
+ try {
579
+ baseline = JSON.parse(baselineRaw);
580
+ } catch {
581
+ fail();
582
+ }
583
+ if (
584
+ baseline.schemaVersion !== 1 ||
585
+ baseline.schemaId !== "frontend-lint-baseline-v1" ||
586
+ baseline.status !== "available" ||
587
+ baseline.commandIdentity?.sha256 !== assessment.commandIdentity.sha256
588
+ ) fail();
589
+ if (assessment.status === "baseline-debt") {
590
+ if (
591
+ !Array.isArray(assessment.currentDiagnostics) ||
592
+ assessment.currentDiagnostics.length === 0 ||
593
+ assessment.currentDiagnostics.length !== assessment.toleratedDiagnosticCount ||
594
+ !Array.isArray(baseline.diagnostics)
595
+ ) fail();
596
+ const key = (item) => JSON.stringify([
597
+ item.file, item.line, item.column, item.severity, item.message, item.ruleId ?? null,
598
+ ]);
599
+ const baselineCounts = new Map();
600
+ for (const item of baseline.diagnostics) {
601
+ const value = key(item);
602
+ baselineCounts.set(value, (baselineCounts.get(value) || 0) + 1);
603
+ }
604
+ const changed = new Set(assessment.writerChangedFiles);
605
+ for (const item of assessment.currentDiagnostics) {
606
+ if (changed.has(item.file)) fail();
607
+ const value = key(item);
608
+ const count = baselineCounts.get(value) || 0;
609
+ if (count === 0) fail();
610
+ baselineCounts.set(value, count - 1);
611
+ }
612
+ }
613
+ if (!Array.isArray(assessment.rawEvidenceRefs) || assessment.rawEvidenceRefs.length === 0) fail();
614
+ for (const ref of assessment.rawEvidenceRefs) {
615
+ if (sha256(readContained(ref.path)) !== ref.sha256) fail();
616
+ }
617
+ process.exit(0);
618
+ NODE
619
+ }
620
+
525
621
  if [[ -f package.json ]]; then
526
622
  if command -v npm >/dev/null 2>&1; then
527
623
  for script in lint typecheck test build; do
528
624
  if has_npm_script "\${script}"; then
625
+ if [[ "\${script}" == "lint" ]] && dag_lint_assessment_allows_skip; then
626
+ echo "==> lint handled by frontend DAG assessment (status: passed or baseline-debt)"
627
+ ran=1
628
+ continue
629
+ fi
529
630
  run_cmd npm run "\${script}"
530
631
  fi
531
632
  done
@@ -674,68 +775,59 @@ function buildManagedAgentsBlock(input) {
674
775
  "### 默认立场",
675
776
  "",
676
777
  "- 仓库是记录系统:决策、契约、计划、验证、报告和交接应进入可追踪文件。",
677
- "- 一次只推进一个有边界、可验证的工作块。",
678
- "- 实现前先搜索现有代码、文档、脚本和测试,避免重复造轮子。",
778
+ "- 一次只推进一个有边界、可验证的工作块;实现前先搜索现有代码、文档、脚本和测试。",
679
779
  "- Shell 验证是完成依据;模型建议不能替代命令证据。",
680
780
  "- 保留无关的用户改动,不要回退自己没有做的修改。",
681
781
  "- 委托模型写入前,必须把写入边界写成结构化 `task.json.allowedPaths` / `task.json.forbiddenPaths`,再审查生成 DAG 的 writer `writeSet`;不要只依赖 `source/执行约束.md` 的自然语言约束。",
682
- "- 本仓库对 openCode 等主会话的定位是 **Compatibility / Operator Assist**:主会话编排 `loop-agent` / `agent-worker` CLI 与只读诊断,**不是**默认实现 agent。",
782
+ "- 本仓库对 openCode 等主会话的定位是 **Compatibility / Operator Assist**:主会话编排 CLI 与只读诊断,**不是**默认实现 agent。",
683
783
  "",
684
784
  "### 主会话硬约束(Compatibility / Operator Assist)",
685
785
  "",
686
- "主会话(含 openCode、Cursor Chat、其他宿主 agent)必须遵守以下硬约束;skills 与本文件是纪律文档,**不能**替代 `task.json` / DAG `writeSet` / runtime 执法。",
786
+ "主会话(含 openCode、Cursor Chat、其他宿主 agent)= **operator-only**;skills 与本文件是纪律文档,**不能**替代 `task.json` / DAG `writeSet` / runtime 执法。",
687
787
  "",
688
788
  "| 类别 | 规则 |",
689
789
  "|---|---|",
690
- "| **角色** | 主会话 = **operator-only**,不是 implementer。 |",
691
- "| **允许** | 调用已发布 `loop-agent` / `agent-worker` CLI;只读 `status` / `dag status` / `dag doctor` / `dag report` / `inspect` / `agent-worker observe`;准备或修正任务源 `source/*` 与 `task.json` 边界;记录 human gate;跑 shell 验证与 handoff。 |",
692
- "| **禁止** | 绕过 `loop-agent` / `agent-worker` CLI,用宿主 Edit/Write/ApplyPatch 等直接改业务实现;在 CLI/DAG 失败后「救火改文件」;用聊天自述代替 shell 验证完成声明。 |",
693
- "| **失败时只允许** | `loop-agent dag doctor` / `dag report` / `dag reconcile-run`(及适用的 `agent-worker task reconcile` / `pool mark-failed`);记 human gate;修正 **任务源 / task.json / DAG 包** 后 re-validate 并 **经 CLI 重跑**。 |",
694
- "| **实现写入** | 业务代码与功能修复 **只** 经受治理 DAG writer(`implement-pi` / `repair-pi` 等)通过 `dag run-task` / `run-dag`(或 worker 子进程调用的同一路径)完成。 |",
790
+ "| **允许** | 已发布 `loop-agent` / `agent-worker` CLI;只读 status/doctor/report/inspect/observe;准备 `source/*` 与 `task.json` 边界;human gate;shell 验证与 handoff。 |",
791
+ "| **禁止** | 绕过 CLI 用宿主 Edit/Write/ApplyPatch 直接改业务实现;CLI/DAG 失败后「救火改文件」;用聊天自述代替 shell 验证。 |",
792
+ "| **失败时** | `dag doctor` / `dag report` / `dag reconcile-run`(及适用 worker reconcile);修正任务源/`task.json`/DAG 后经 CLI 重跑。 |",
793
+ "| **实现写入** | 业务代码 **只** 经受治理 DAG writer(`implement-pi` / `repair-pi`)经 `dag run-task` / `run-dag`。 |",
695
794
  "",
696
795
  "**永远不要**:`loop-agent` / `agent-worker` 失败 ⇒ 主会话直接改仓库实现。",
697
796
  "",
698
797
  "### 自然语言入口路由",
699
798
  "",
700
- "用户用中文表达意图时,按以下三类入口路由;只读与写入必须严格区分。",
701
- "",
702
799
  "| 用户表达 | 入口 | 执行动作 |",
703
800
  "|---|---|---|",
704
801
  "| loop-agent 初始化 / loop agent 初始化 / loop agent初始化 / 初始化 loop-agent | 初始化 | 完成确定性初始化闭环 |",
705
802
  "| 初始化更新校验 / 检查初始化更新 / loop-agent 初始化更新校验 / loop agent初始化更新校验 | 更新校验 | 只读报告,不写入 |",
706
- "| 初始化对齐 / 升级后对齐 / reconcile 初始化 / loop-agent 初始化对齐 | 升级对齐 | 自动应用确定性安全动作;活跃运行或人工决策时零写入 |",
707
- "| 初始化安全更新 / 应用初始化更新 / loop-agent 初始化安全更新 / loop agent初始化安全更新 | 安全更新 | check-update 后只执行确定性安全动作 |",
803
+ "| 初始化对齐 / 升级后对齐 / reconcile 初始化 / loop-agent 初始化对齐 | 升级对齐 | 自动应用确定性安全动作;活跃 DAG/Worker 或人工决策时零写入 |",
804
+ "| 初始化安全更新 / 应用初始化更新 / loop-agent 初始化安全更新 / loop agent初始化安全更新 | 安全更新 | check-update,再只执行确定性安全动作 |",
708
805
  "",
709
- "**更新校验(只读)**:用户说“初始化更新校验”“检查初始化更新”时,只读执行下面命令,汇报 deterministic actions、model merge、human decisions 和 next steps。**这一步不得自动执行 `apply-safe` 或模型合并。**",
806
+ "**更新校验(只读)**:只读执行下面命令;**不得自动**执行 `apply-safe` 或模型合并。",
710
807
  "",
711
808
  "```bash",
712
809
  "loop-agent init check-update --repo-root . --markdown",
713
810
  "```",
714
811
  "",
715
- "**升级对齐(写入型统一入口)**:用户说“初始化对齐”“升级后对齐”时,执行 `loop-agent init reconcile --repo-root .`。该入口不自动 bootstrap surface、不自动模型合并;存在活跃 DAG/Worker、human decisions 或无法确认 Worker 状态时必须零写入并报告状态。",
812
+ "**升级对齐**:`loop-agent init reconcile --repo-root .`;存在活跃 DAG/Worker、human decisions 时必须零写入。",
716
813
  "",
717
- "**安全更新(写入型)**:用户明确说“初始化安全更新”“应用初始化更新”时,先 check-update,再执行确定性安全动作;surface 缺失时先 bootstrap,存在 human decisions 时停下等用户决策。model merge 任务必须带 allowed/forbidden paths,且不得自动决策。安全更新完成后必须复查 `loop-agent init doctor --repo-root .`、`loop-agent inspect --repo-root .`、`loop-agent docs audit --repo-root .` 与目标项目治理检查(如 `bash scripts/check-repo.sh`)。",
814
+ "**安全更新**:先 check-update,再 `apply-safe`;surface 缺失才 `--bootstrap-surface`。完成后复查 `init doctor` / `inspect` / `docs audit` 与目标治理检查。",
718
815
  "",
719
816
  "```bash",
720
817
  "loop-agent init check-update --repo-root . --markdown",
721
818
  "loop-agent init update --repo-root . --apply-safe",
722
819
  "```",
723
820
  "",
724
- "仅当 check-update 报告 surface 缺失时才执行 `loop-agent init update --repo-root . --bootstrap-surface`。",
725
- "",
726
821
  "### 初始化闭环",
727
822
  "",
728
823
  `当用户要求初始化 loop-agent(“初始化 loop-agent”“loop agent 初始化”“loop agent初始化”“loop-agent 初始化”),或发现 \`README.md\` / \`${input.governanceRoot}/verification-matrix.md\` 仍停留在通用初始化状态时,模型应在同一轮工作中完成初始化闭环,不要把补全文档和验证矩阵留给用户交互。`,
729
824
  "",
730
- "标准流程:",
731
- "",
732
- "1. 运行 `loop-agent init instructions --repo-root .` 获取完整初始化指引。",
733
- "2. 运行 `loop-agent init --repo-root . --profile full --merge` 完成确定性脚手架。",
734
- "3. 立即读取目标项目的 README、manifest/build/config 文件和顶层源码/模块目录,识别项目类型、模块、运行入口和验证命令。",
735
- `4. 基于真实项目文件补全或修正根 \`README.md\`、\`${input.governanceRoot}/verification-matrix.md\`,必要时适配 \`scripts/ci-tests.sh\`。`,
736
- "5. 运行 `loop-agent init doctor --repo-root .`、`loop-agent inspect --repo-root .`、`loop-agent docs audit --repo-root .` 和 quick verification。",
825
+ "1. 运行 `loop-agent init instructions --repo-root .`。",
826
+ "2. 运行 `loop-agent init --repo-root . --profile full --merge`。",
827
+ `3. 基于真实项目文件补全根 \`README.md\`、\`${input.governanceRoot}/verification-matrix.md\`,必要时适配 \`scripts/ci-tests.sh\`。`,
828
+ "4. 复查 `init doctor` / `inspect` / `docs audit` 与 quick verification。",
737
829
  "",
738
- "如果仓库信息不足,写明“尚未从仓库文件中识别到 ...”以及下一步需要的事实;不要留下泛化占位符,也不要只提示用户稍后手填。",
830
+ "信息不足时写明尚未识别到的事实;不要留下泛化占位符。",
739
831
  "",
740
832
  "### 文档收敛",
741
833
  "",
@@ -743,21 +835,10 @@ function buildManagedAgentsBlock(input) {
743
835
  "",
744
836
  "### 开始顺序",
745
837
  "",
746
- "改文件前先完成:",
747
- "",
748
- "1. 运行 `pwd`。",
749
- "2. 阅读 `README.md`。",
750
- "3. 阅读 `harness.json`。",
751
- `4. 阅读 \`${input.governanceRoot}/README.md\`。`,
752
- `5. 实现类工作继续阅读 \`${input.governanceRoot}/development-principles.md\`、\`${input.governanceRoot}/feature-workflow.md\` 和 \`${input.governanceRoot}/verification-matrix.md\`。`,
753
- `6. 涉及测试、验证声明或调试时继续阅读 \`${input.governanceRoot}/harness-methodology-tdd.md\`、\`${input.governanceRoot}/harness-methodology-verification.md\` 和 \`${input.governanceRoot}/harness-methodology-debugging.md\`。`,
754
- "7. 查看最近提交、相关 plan/progress/report,并检查 `git status --short --branch`。",
755
- "8. 运行与本次任务相关的最小基线验证。",
838
+ `pwd → \`README.md\` → \`harness.json\` → \`${input.governanceRoot}/README.md\` → 实现类再读 principles/feature-workflow/verification-matrix;测试纪律读 harness-methodology-*;\`git status\`;最小基线验证。`,
756
839
  "",
757
840
  "### Agent DAG 路径",
758
841
  "",
759
- "默认使用 Agent DAG 作为实现工作流:",
760
- "",
761
842
  "```bash",
762
843
  "loop-agent new-task <task-id> \"任务标题\"",
763
844
  "# write .harness/tasks/<task-id>/source/需求.md",
@@ -767,64 +848,40 @@ function buildManagedAgentsBlock(input) {
767
848
  "loop-agent run-dag --dag <temp-dir>/<task-id>-dag.json --cwd .",
768
849
  "```",
769
850
  "",
770
- "任务 source 是必需项。`source/需求.md` 写目标、范围、非目标、验收标准和相关链接;`source/执行约束.md` 写允许路径、禁止路径、受保护变更、不变量、预期验证和失败条件。",
771
- "",
772
- "委托写入前,还要把允许/禁止路径同步到 `.harness/tasks/<task-id>/task.json` 的 `allowedPaths` / `forbiddenPaths` 字段,并在执行前审查生成 DAG 的 writer `writeSet` 是否窄且准确。",
851
+ "`source/需求.md` 与 `source/执行约束.md` 必需;写入前同步 `task.json.allowedPaths` / `task.json.forbiddenPaths` 并审查 writer `writeSet`。",
773
852
  "",
774
853
  "凡是影响项目公共契约、执行入口、交付流水线、自动化/治理、数据模型、安全或权限模型、跨模块行为、用户可见工作流的改动,都必须在编辑实现文件前先创建任务、写好两个 source 文件、生成 DAG,并审查 DAG/writeSet。",
775
854
  "",
776
- "执行 DAG 前必须审查 profile routing、governance profile、writer 的 `writeSet`、`allowedPaths`、`forbiddenPaths`、shell verification 和 decision gate mode。不要执行占位或过宽的写入范围。",
777
- "",
778
855
  "### 任务类型路由(taskKind)",
779
856
  "",
780
857
  "- 用户明确提出后端测试、接口/API 测试、pytest,或语境明确为后端的自动化测试时,必须把 `.harness/tasks/<task-id>/task.json` 的 `taskKind` 设置为 `\"backend-test\"`,不得保留默认 `standard`。",
781
- "- `backend-test` 是 `taskKind`,不是 `--profile` 的可选值;运行 `dag run-task` 时继续使用 `--profile auto`,也可按需显式选择 `minimal` / `standard` / `reviewed` / `supervised`,不要把业务模板名当作 profile。",
858
+ "- `backend-test` 是 `taskKind`,不是 `--profile` 的可选值;运行 `dag run-task` 时继续使用 `--profile auto`。",
782
859
  "- 仅出现“自动化测试”且无法判断前后端时,先阅读任务源与目标项目技术栈再决定,禁止无条件路由到 `backend-test`。",
783
860
  "",
784
861
  "### 运行看板(只读)",
785
862
  "",
786
- "启动统一 Operator Console(含只读 Inspect):",
787
- "",
788
863
  "```bash",
789
864
  "agent-worker console serve --repo . --port 8790",
790
865
  "```",
791
866
  "",
792
- "浏览器打开 `http://127.0.0.1:8790/`;检视面为 `http://127.0.0.1:8790/inspect/`。默认绑定本机 `127.0.0.1`;可用 `--host 0.0.0.0` 做局域网访问(无网络鉴权,仅可信网络),`--debug` 输出请求日志。不要直接暴露到公开网络。`agent-worker observe serve` 仅为兼容入口。",
867
+ "浏览器打开 `http://127.0.0.1:8790/`;检视面为 `http://127.0.0.1:8790/inspect/`。默认绑定本机 `127.0.0.1`;不要直接暴露到公开网络。`agent-worker observe serve` 仅为兼容入口。",
793
868
  "",
794
869
  "### DAG 诊断与收口",
795
870
  "",
796
- "执行后优先使用 `loop-agent dag report --run-id <run-id> --markdown` 读取 run facts;失败或 paused run 使用 `loop-agent dag doctor --run-id <run-id> --markdown` 诊断。需要生命周期对齐时用 `loop-agent dag reconcile-run --run-id <run-id>`(先只读检查,显式 action 才写入)。失败 DAG run 不应写成成功 closeout,应使用 `loop-agent dag closeout-draft --run-id <run-id>` 生成 failure handoff,保留 what failed、evidence、classification、recommended follow-up、safe retry conditions 和 human decision needed。",
797
- "",
798
- "失败恢复默认序列:doctor/report → classify → reconcile 或 replan → CLI 重跑 → shell verify。**禁止**把主会话直接 Edit 业务代码当作恢复手段。",
799
- "",
800
- "### 运行态与 Skills",
871
+ " `loop-agent dag report --run-id <run-id> --markdown` facts;失败/paused `loop-agent dag doctor --run-id <run-id> --markdown`;生命周期对齐用 `loop-agent dag reconcile-run`。失败 run `dag closeout-draft` 生成 failure handoff,不要写成成功 closeout。",
801
872
  "",
802
- "- `.harness/tasks/` 保存任务状态和 source 材料。",
803
- "- `.harness/dag-runs/` 与 `.harness/runs/` 保存运行事实;已完成事实只读。",
804
- "- `.agents/skills/` 保存目标项目本地 skill 指令;本地缺失时 runtime 可回退到 npm 包内置 skills。",
805
- "- 示例内置在工具中;优先使用 `loop-agent examples list` 和 `loop-agent examples show <name>` 查看,不默认提交到目标仓库。",
806
- "",
807
- "### 验证",
808
- "",
809
- `使用 \`${input.governanceRoot}/verification-matrix.md\` 选择验证命令。常用门禁:`,
810
- "",
811
- "```bash",
812
- "bash scripts/check-repo.sh",
813
- "bash scripts/ci-governance.sh",
814
- "bash scripts/ci-tests.sh",
815
- "bash scripts/ci.sh",
816
- "loop-agent inspect",
817
- "loop-agent doctor",
818
- "loop-agent docs audit",
819
- "```",
873
+ "恢复:doctor/report → classify → reconcile/replan CLI 重跑 → shell verify。**禁止**把主会话直接 Edit 业务代码当作恢复手段。",
820
874
  "",
821
- "`scripts/ci-tests.sh` 必须反映目标项目真实语言和工具链。初始化生成版本会保守探测常见入口;当已知项目专属命令时,应按目标项目实际情况适配。",
875
+ "### 运行态与验证",
822
876
  "",
823
- "Windows 上运行 `scripts/*.sh` 时使用 Git Bash 或兼容 Bash。实际文件操作使用平台原生路径;`/` 仅用于稳定仓库引用、Markdown/JSON 证据引用和 glob 约定。",
877
+ "- `.harness/tasks/`、`.harness/dag-runs/`、`.harness/runs/` 保存运行事实;已完成事实只读。",
878
+ "- `.agents/skills/` 为本地 skills;缺失时可回退 npm 包内置。",
879
+ `- 验证命令选择:\`${input.governanceRoot}/verification-matrix.md\`。常用:\`bash scripts/check-repo.sh\`、\`bash scripts/ci.sh\`、\`loop-agent inspect\`、\`loop-agent doctor\`、\`loop-agent docs audit\`。`,
880
+ "- `scripts/ci-tests.sh` 必须反映目标项目真实工具链。Windows 脚本用 Git Bash;仓库引用用 `/`。",
824
881
  "",
825
882
  "### 交接",
826
883
  "",
827
- `较大的交接应说明改了什么、为什么这样改、验证命令和结果、影响到的 docs/tests/scripts/contracts、剩余风险和下一步。长期结论应进入 \`${input.governanceRoot}/progress\`、\`${input.governanceRoot}/reports\`、\`${input.governanceRoot}/exec-plans\`、\`${input.governanceRoot}/decisions\`、测试、脚本或模板。`,
884
+ `交接写清变更、原因、验证证据、影响面、风险与下一步;长期结论进入 \`${input.governanceRoot}/progress\`、reportsexec-plansdecisions。`,
828
885
  MANAGED_BLOCK_END,
829
886
  ].join("\n");
830
887
  }
@@ -1276,6 +1333,10 @@ async function buildDesiredSurfaceContent(input) {
1276
1333
  });
1277
1334
  if (generated !== undefined)
1278
1335
  return { content: generated };
1336
+ const mappedSourcePath = path.join(input.assetRoot, "docs", coreDocSourceForTarget(doc));
1337
+ if (await exists(mappedSourcePath)) {
1338
+ return { content: await readFile(mappedSourcePath, "utf-8"), sourcePath: mappedSourcePath };
1339
+ }
1279
1340
  }
1280
1341
  const sourcePath = path.join(input.assetRoot, manifestPath);
1281
1342
  if (await exists(sourcePath))
@@ -1485,6 +1546,10 @@ async function expectedLegacyInitFileSha(input) {
1485
1546
  if (!input.legacyPath.startsWith("docs/") && !input.legacyPath.startsWith("skills/")) {
1486
1547
  return undefined;
1487
1548
  }
1549
+ if (input.legacyPath.startsWith("docs/")) {
1550
+ const doc = input.legacyPath.slice("docs/".length);
1551
+ return fileSha256IfExists(path.join(input.assetRoot, "docs", coreDocSourceForTarget(doc)));
1552
+ }
1488
1553
  return fileSha256IfExists(path.join(input.assetRoot, input.legacyPath));
1489
1554
  }
1490
1555
  function collectSafeRetiredDirectories(paths) {
@@ -1815,6 +1880,7 @@ function buildTargetDocsReadme(input) {
1815
1880
  "- `progress/README.md` - 进度交接日志 / progress handoff logs",
1816
1881
  "- `reports/README.md` - 验证与审计报告 / verification and audit reports",
1817
1882
  "- `decisions/README.md` - 架构决策 / architecture decisions",
1883
+ "- `templates/README.md` - 模板入口与类别说明 / template entrypoint and category guide",
1818
1884
  "- `templates/` - 可复用的计划、报告与 DAG 模板 / reusable planning, reporting, and DAG templates",
1819
1885
  "- `templates/production-readiness-checklist.md` - 低/中风险单仓库 DAG 任务的 production readiness 检查清单 / production readiness checklist for low/medium-risk single-repo DAG work",
1820
1886
  "- `templates/worker-dogfood-setup.md` - 发布控制器下的真实 Worker sample setup / real Worker sample setup with a published controller",
@@ -1919,10 +1985,34 @@ function buildTargetDevelopmentPrinciples(input) {
1919
1985
  "9. Do not add placeholders as completed implementation.",
1920
1986
  "10. Prefer existing local project patterns before adding new abstractions.",
1921
1987
  "",
1988
+ "## Task Slicing: Vertical Tracer Bullets First",
1989
+ "",
1990
+ "Principle 1 covers **granularity** (one bounded block). This section covers **shape**: each slice should cross the real integration layers the work needs and leave an independently verifiable narrow loop.",
1991
+ "",
1992
+ "- Every slice needs its own acceptance criteria, verification commands, and failure conditions.",
1993
+ "- Prefer vertical tracer bullets over horizontal layering. Paths like \"schema → API → UI → tests\" are *possible* examples only; do not assume every project has those layers.",
1994
+ "- Horizontal anti-patterns: finish all of one layer before the next; or write every test first, then implement everything.",
1995
+ "- For behavior changes, use one failing test → minimal implementation → green → next behavior. Do not batch all RED then all GREEN.",
1996
+ "- Split large features into multiple independently runnable tasks/DAGs instead of one oversized writer across every layer.",
1997
+ "",
1998
+ "### Autonomy vs Governance (independent layers)",
1999
+ "",
2000
+ "| Dimension | Meaning | How to decide |",
2001
+ "|---|---|---|",
2002
+ "| Autonomy | Whether the slice needs synchronous human judgment, external access, or non-automatable decisions | Declare AFK/HITL in Contract, open questions, or human gate signals |",
2003
+ "| Governance | How strong review, repair, write-set, and verification gates must be | Default `--profile auto`; route to `minimal` / `standard` / `reviewed` / `supervised` by risk and delivery signals |",
2004
+ "",
2005
+ "- AFK does not mean `minimal` is required; ordinary automatable work may land on `standard` or `reviewed`.",
2006
+ "- HITL does not mean choosing `supervised` alone yields a correct human decision; require a concrete pause reason / decision gate.",
2007
+ "- Use `minimal` only for a single narrow writer, deterministic post shell verification, and no escalation signals.",
2008
+ "- High-risk, public-contract, init/runtime/CI/governance surface, or real human judgment gates should escalate via `auto`, or explicitly choose `reviewed` / `supervised`.",
2009
+ "",
1922
2010
  "## Target Project Adaptation",
1923
2011
  "",
1924
2012
  "The initialized scripts provide language-neutral governance. The initialization model should adapt project-specific verification commands after reading the target project's actual files and toolchain.",
1925
2013
  "",
2014
+ "Repo-local skills live under `.agents/skills/` (mirrored from the package `skills/` fallback).",
2015
+ "",
1926
2016
  ].join("\n");
1927
2017
  }
1928
2018
  function buildTargetFeatureWorkflow(input) {
@@ -2105,6 +2195,28 @@ function buildGovernanceDirectoryReadme(input) {
2105
2195
  .split("/")
2106
2196
  .map((part) => part.replace(/-/g, " "))
2107
2197
  .join(" / ");
2198
+ if (input.dir === "decisions" || input.dir.endsWith("/decisions")) {
2199
+ return [
2200
+ `# ${title}`,
2201
+ "",
2202
+ `This directory stores ${input.projectName} architecture decision records (ADRs).`,
2203
+ "",
2204
+ "## When to write an ADR",
2205
+ "",
2206
+ "Write an ADR only when **all three** are true:",
2207
+ "",
2208
+ "1. **Hard to reverse** — changing the decision later has meaningful cost.",
2209
+ "2. **Surprising without context** — a future reader will ask why this path was chosen.",
2210
+ "3. **Real trade-off** — genuine alternatives existed and one was chosen for specific reasons.",
2211
+ "",
2212
+ "If any condition is missing, skip the ADR. Temporary scheduling, obvious implementation choices, and facts with no alternatives do not belong here.",
2213
+ "",
2214
+ "Use the ADR template under this governance root's `templates/adr.md` (or the package template when projecting). Update this README index when adding a new ADR.",
2215
+ "",
2216
+ "初始化只创建目录契约,不复制 loop-agent 源仓库的历史决策正文。目标项目中的具体 ADR 应由后续任务按真实取舍生成。",
2217
+ "",
2218
+ ].join("\n");
2219
+ }
2108
2220
  return [
2109
2221
  `# ${title}`,
2110
2222
  "",
@@ -2250,11 +2362,11 @@ export async function initializeLoopAgentProject(options) {
2250
2362
  skipped,
2251
2363
  });
2252
2364
  for (const doc of CORE_DOC_FILES) {
2253
- const generated = buildGeneratedCoreDoc({ doc, projectName, governanceRoot });
2365
+ const generated = buildGeneratedCoreDoc({ doc: doc.target, projectName, governanceRoot });
2254
2366
  if (generated) {
2255
2367
  await writeTextIfMissing({
2256
2368
  repoRoot,
2257
- relativePath: path.join(governanceRoot, doc),
2369
+ relativePath: path.join(governanceRoot, doc.target),
2258
2370
  content: generated,
2259
2371
  merge,
2260
2372
  written,
@@ -2265,8 +2377,8 @@ export async function initializeLoopAgentProject(options) {
2265
2377
  await copyFileIfMissing({
2266
2378
  assetRoot,
2267
2379
  repoRoot,
2268
- sourceRelativePath: path.join("docs", doc),
2269
- targetRelativePath: path.join(governanceRoot, doc),
2380
+ sourceRelativePath: path.join("docs", doc.source),
2381
+ targetRelativePath: path.join(governanceRoot, doc.target),
2270
2382
  merge,
2271
2383
  written,
2272
2384
  skipped,
@@ -1,5 +1,6 @@
1
- import { readFile, readdir } from 'node:fs/promises';
2
- import path from 'node:path';
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { buildContextUsageReport, formatContextUsageReportHuman, parseStatsContextArgs, } from "../application/context-usage/skill-resolution-stats.js";
3
4
  function percentile(values, p) {
4
5
  if (values.length === 0)
5
6
  return null;
@@ -12,8 +13,8 @@ function formatMs(ms) {
12
13
  return `${(ms / 60_000).toFixed(1)} min`;
13
14
  return `${(ms / 1_000).toFixed(1)}s`;
14
15
  }
15
- export async function runStats(repoRoot) {
16
- const tasksDir = path.join(repoRoot, '.harness', 'tasks');
16
+ async function runLegacyStepStats(repoRoot) {
17
+ const tasksDir = path.join(repoRoot, ".harness", "tasks");
17
18
  const stepRecords = {};
18
19
  let taskDirs = [];
19
20
  try {
@@ -21,20 +22,20 @@ export async function runStats(repoRoot) {
21
22
  taskDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
22
23
  }
23
24
  catch {
24
- console.log('no task directories found');
25
+ console.log("no task directories found");
25
26
  return;
26
27
  }
27
28
  for (const taskId of taskDirs) {
28
- const jsonlPath = path.join(tasksDir, taskId, 'logs', 'executor.jsonl');
29
+ const jsonlPath = path.join(tasksDir, taskId, "logs", "executor.jsonl");
29
30
  try {
30
- const content = await readFile(jsonlPath, 'utf-8');
31
+ const content = await readFile(jsonlPath, "utf-8");
31
32
  for (const line of content.split(/\r?\n/)) {
32
33
  const trimmed = line.trim();
33
34
  if (!trimmed)
34
35
  continue;
35
36
  try {
36
37
  const record = JSON.parse(trimmed);
37
- if (typeof record.step === 'string' && typeof record.durationMs === 'number') {
38
+ if (typeof record.step === "string" && typeof record.durationMs === "number") {
38
39
  const step = record.step;
39
40
  if (!stepRecords[step])
40
41
  stepRecords[step] = [];
@@ -55,7 +56,7 @@ export async function runStats(repoRoot) {
55
56
  // skip missing executor.jsonl
56
57
  }
57
58
  }
58
- const stepOrder = ['analyze', 'plan', 'spec', 'implement', 'verify', 'retrospective'];
59
+ const stepOrder = ["analyze", "plan", "spec", "implement", "verify", "retrospective"];
59
60
  let totalRecords = 0;
60
61
  let totalTimedOut = 0;
61
62
  for (const step of stepOrder) {
@@ -75,10 +76,38 @@ export async function runStats(repoRoot) {
75
76
  console.log(` max : ${formatMs(Math.max(...durations))}`);
76
77
  if (timedOutCount > 0)
77
78
  console.log(` timedOut: ${timedOutCount}`);
78
- console.log('');
79
+ console.log("");
79
80
  }
80
- console.log(`${'='.repeat(40)}`);
81
+ console.log(`${"=".repeat(40)}`);
81
82
  console.log(`total runs: ${totalRecords}`);
82
83
  if (totalTimedOut > 0)
83
84
  console.log(`total timed out: ${totalTimedOut}`);
84
85
  }
86
+ async function runContextStats(repoRoot, args) {
87
+ const parsed = parseStatsContextArgs(args);
88
+ const report = await buildContextUsageReport(repoRoot, { last: parsed.last });
89
+ if (parsed.json) {
90
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
91
+ }
92
+ else {
93
+ console.log(formatContextUsageReportHuman(report));
94
+ }
95
+ if (!report.ok)
96
+ process.exitCode = 1;
97
+ }
98
+ /**
99
+ * `stats` — legacy step-duration histogram (default, no subcommand).
100
+ * `stats context [--last N] [--json]` — skill-resolution / outcome telemetry.
101
+ */
102
+ export async function runStats(repoRoot, args = []) {
103
+ const [subcommand, ...rest] = args;
104
+ if (subcommand === undefined) {
105
+ await runLegacyStepStats(repoRoot);
106
+ return;
107
+ }
108
+ if (subcommand === "context") {
109
+ await runContextStats(repoRoot, rest);
110
+ return;
111
+ }
112
+ throw new Error(`unknown stats subcommand: ${subcommand}\nusage: stats | stats context [--last <n>] [--json]`);
113
+ }
@@ -19,6 +19,8 @@ export const DAG_PI_MODEL_PROVIDERS = {
19
19
  "gpt-5.3-codex-spark": "wizard-local",
20
20
  "gpt-5.5": "wizard-local",
21
21
  "glm-5.2": "wizard-local",
22
+ "deepseek-v4-flash": "deepseek",
23
+ "deepseek-v4-pro": "deepseek",
22
24
  };
23
25
  const SAFE_PI_STEPS = new Set([
24
26
  "analyze",