@tea-agent/loop-agent 0.25.5 → 0.26.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.
Files changed (173) hide show
  1. package/AGENTS.md +2 -1
  2. package/CHANGELOG.md +1020 -986
  3. package/bin/loop-agent.js +21 -21
  4. package/dist/commands/cursor-prompt.js +6 -6
  5. package/dist/commands/init.js +68 -129
  6. package/dist/commands/loop-benchmark.js +11 -11
  7. package/dist/commands/pi-reuse-benchmark.js +16 -16
  8. package/dist/executors/dag-pi-executor.js +26 -20
  9. package/dist/executors/model-routing.js +34 -18
  10. package/dist/executors/shell-executor.js +1 -1
  11. package/dist/governance/manifest-types.js +33 -5
  12. package/dist/sidecars/cursor-prompt/executor.js +1 -1
  13. package/dist/task/task-demand-routing.js +3 -1
  14. package/dist/worker/console/chat/model-resolver.js +15 -3
  15. package/dist/worker/observe/static/constants.js +3 -2
  16. package/dist/worker/observe/static/copy.js +67 -67
  17. package/dist/worker/observe/static/dag-layout.d.ts +31 -31
  18. package/dist/worker/observe/static/dag-layout.js +83 -83
  19. package/dist/worker/observe/static/dag-model.js +1 -0
  20. package/dist/worker/observe/static/dom.js +220 -220
  21. package/dist/worker/observe/static/relations.js +133 -133
  22. package/dist/worker/observe/static/router.js +93 -93
  23. package/dist/worker/observe/static/run-processing.js +148 -148
  24. package/dist/worker/observe/static/styles.css +182 -42
  25. package/dist/worker/observe/static/views/batch.js +227 -227
  26. package/dist/worker/observe/static/views/dag-graph.js +172 -172
  27. package/dist/worker/observe/static/views/failures.js +143 -143
  28. package/dist/worker/observe/static/views/feature.js +492 -492
  29. package/dist/worker/observe/static/views/run.js +453 -453
  30. package/dist/worker/observe/static/views/shell.js +7 -7
  31. package/dist/worker/observe/static/views/timeline.js +163 -163
  32. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +362 -45
  33. package/dist/workflows/dag/backend-test-case-manifest.js +20 -0
  34. package/dist/workflows/dag/backend-test-markdown-workflow.js +17 -0
  35. package/dist/workflows/dag/canvas-observer.js +275 -275
  36. package/dist/workflows/dag/init-hybrid.js +18 -15
  37. package/dist/workflows/dag/lifecycle.js +40 -30
  38. package/dist/workflows/dag/node-execution.js +13 -0
  39. package/dist/workflows/dag/types.js +59 -19
  40. package/docs/init-surface.manifest.json +3 -1
  41. package/docs/skills/README.md +7 -7
  42. package/docs/templates/README.md +1 -0
  43. package/docs/templates/adr.md +60 -60
  44. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
  45. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
  46. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
  47. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
  48. package/docs/templates/agent-dag-report.schema.json +473 -473
  49. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
  50. package/docs/templates/backend-test-dag.json +9 -9
  51. package/docs/templates/backend-test-result.schema.json +99 -99
  52. package/docs/templates/feature-spec.md +53 -53
  53. package/docs/templates/frontend-design-contract.md +42 -42
  54. package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -17
  55. package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -16
  56. package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -16
  57. package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -16
  58. package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -16
  59. package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -16
  60. package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -21
  61. package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -29
  62. package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -28
  63. package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -28
  64. package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -29
  65. package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -27
  66. package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -28
  67. package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -28
  68. package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -27
  69. package/docs/templates/frontend-eval/metrics.md +138 -138
  70. package/docs/templates/frontend-eval/smoke-targets.md +53 -53
  71. package/docs/templates/frontend-task-constraints.md +35 -35
  72. package/docs/templates/frontend-task-requirement.md +70 -70
  73. package/docs/templates/harness.schema.json +29 -7
  74. package/docs/templates/init-evolution-review.md +35 -35
  75. package/docs/templates/init-managed-agents.md +137 -0
  76. package/docs/templates/interactive-ui-round2-experiment.md +66 -66
  77. package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
  78. package/docs/templates/knowledge-sync-dag.json +178 -178
  79. package/docs/templates/knowledge-sync-draft.schema.json +71 -71
  80. package/docs/templates/product-line/closeout.yaml +9 -9
  81. package/docs/templates/product-line/design.md +13 -13
  82. package/docs/templates/product-line/links.md +10 -10
  83. package/docs/templates/product-line/requirement.md +17 -17
  84. package/docs/templates/product-line/test-plan.md +7 -7
  85. package/docs/templates/production-readiness-checklist.md +57 -57
  86. package/docs/templates/project-start-checklist.md +9 -9
  87. package/docs/templates/qa-report.md +48 -48
  88. package/docs/templates/sprint-contract.md +29 -29
  89. package/docs/templates/worker-dogfood-evidence.md +80 -80
  90. package/docs/templates/worker-dogfood-setup.md +68 -68
  91. package/harness.json +1 -2
  92. package/package.json +1 -1
  93. package/scripts/kb-bootstrap-init-skeleton.sh +0 -0
  94. package/scripts/kb-graph-incremental-prepare.mjs +386 -386
  95. package/scripts/kb-graph-materialize.mjs +105 -105
  96. package/scripts/kb-graph-promote.mjs +164 -164
  97. package/scripts/kb-query.mjs +554 -554
  98. package/skills/ai-engineering-context/SKILL.md +48 -48
  99. package/skills/analyze-product-dependencies/SKILL.md +67 -67
  100. package/skills/analyze-product-dependencies/agents/openai.yaml +4 -4
  101. package/skills/analyze-product-dependencies/references/api-documentation-schema.md +30 -30
  102. package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +28 -28
  103. package/skills/analyze-product-dependencies/references/example.md +76 -76
  104. package/skills/analyze-product-dependencies/references/forward-test-cases.md +35 -35
  105. package/skills/analyze-product-dependencies/references/input-contract.md +11 -11
  106. package/skills/analyze-product-dependencies/references/scouting-rules.md +61 -61
  107. package/skills/analyze-product-dependencies/scripts/test-validators.mjs +267 -267
  108. package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +101 -101
  109. package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +142 -142
  110. package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +76 -76
  111. package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +146 -146
  112. package/skills/analyze-product-requirements/SKILL.md +90 -90
  113. package/skills/analyze-product-requirements/agents/openai.yaml +4 -4
  114. package/skills/analyze-product-requirements/references/acceptance-criteria.md +91 -91
  115. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +56 -56
  116. package/skills/analyze-product-requirements/references/example.md +86 -86
  117. package/skills/analyze-product-requirements/references/forward-test-cases.md +66 -66
  118. package/skills/analyze-product-requirements/references/product-analysis-schema.md +32 -32
  119. package/skills/analyze-product-requirements/references/product-requirement-schema.md +33 -33
  120. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +35 -35
  121. package/skills/analyze-product-requirements/scripts/test-validators.mjs +193 -193
  122. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +69 -69
  123. package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +97 -97
  124. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +98 -98
  125. package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +156 -156
  126. package/skills/browser-tools/browser-content.js +103 -103
  127. package/skills/browser-tools/browser-cookies.js +35 -35
  128. package/skills/browser-tools/browser-eval.js +53 -53
  129. package/skills/browser-tools/browser-hn-scraper.js +108 -108
  130. package/skills/browser-tools/browser-nav.js +44 -44
  131. package/skills/browser-tools/browser-pick.js +162 -162
  132. package/skills/browser-tools/browser-screenshot.js +34 -34
  133. package/skills/browser-tools/browser-start.js +86 -86
  134. package/skills/browser-tools/package-lock.json +2556 -2556
  135. package/skills/browser-tools/package.json +19 -19
  136. package/skills/code-review-core/SKILL.md +20 -20
  137. package/skills/codebase-scout/SKILL.md +19 -19
  138. package/skills/grill-me/SKILL.md +10 -10
  139. package/skills/loop-agent/references/README.md +67 -67
  140. package/skills/loop-agent/references/command-reference.md +5 -4
  141. package/skills/loop-agent/references/docs-converge.md +126 -126
  142. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  143. package/skills/loop-agent/references/learned/README.md +21 -21
  144. package/skills/loop-agent/references/long-running-loop.md +57 -57
  145. package/skills/loop-agent/references/model-routing.md +2 -0
  146. package/skills/loop-agent/references/one-shot-runs.md +85 -85
  147. package/skills/loop-agent/references/pi-prompt.md +23 -23
  148. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
  149. package/skills/playwright-cli/SKILL.md +420 -420
  150. package/skills/playwright-cli/references/element-attributes.md +23 -23
  151. package/skills/playwright-cli/references/playwright-tests.md +39 -39
  152. package/skills/playwright-cli/references/request-mocking.md +87 -87
  153. package/skills/playwright-cli/references/running-code.md +241 -241
  154. package/skills/playwright-cli/references/session-management.md +225 -225
  155. package/skills/playwright-cli/references/storage-state.md +275 -275
  156. package/skills/playwright-cli/references/test-generation.md +433 -433
  157. package/skills/playwright-cli/references/tracing.md +139 -139
  158. package/skills/playwright-cli/references/video-recording.md +143 -143
  159. package/skills/requesting-code-review/SKILL.md +101 -101
  160. package/skills/requesting-code-review/code-reviewer.md +168 -168
  161. package/skills/systematic-debugging/CREATION-LOG.md +119 -119
  162. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
  163. package/skills/systematic-debugging/condition-based-waiting.md +115 -115
  164. package/skills/systematic-debugging/defense-in-depth.md +122 -122
  165. package/skills/systematic-debugging/find-polluter.sh +63 -63
  166. package/skills/systematic-debugging/root-cause-tracing.md +169 -169
  167. package/skills/systematic-debugging/test-academic.md +14 -14
  168. package/skills/systematic-debugging/test-pressure-1.md +58 -58
  169. package/skills/systematic-debugging/test-pressure-2.md +68 -68
  170. package/skills/systematic-debugging/test-pressure-3.md +69 -69
  171. package/skills/using-git-worktrees/SKILL.md +215 -215
  172. package/skills/verification-before-completion/SKILL.md +154 -154
  173. package/skills/webapp-testing/SKILL.md +19 -19
package/bin/loop-agent.js CHANGED
@@ -1,21 +1,21 @@
1
- #!/usr/bin/env node
2
- import { existsSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath, pathToFileURL } from "node:url";
5
-
6
- const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
7
- const cliEntry = join(packageRoot, "dist", "cli.js");
8
-
9
- if (!existsSync(cliEntry)) {
10
- console.error(
11
- `loop-agent: cannot find built CLI at ${cliEntry}. Run \`npm run build\` before using the package bin.`,
12
- );
13
- process.exit(1);
14
- }
15
-
16
- try {
17
- await import(pathToFileURL(cliEntry).href);
18
- } catch (error) {
19
- console.error(error instanceof Error ? error.message : String(error));
20
- process.exit(1);
21
- }
1
+ #!/usr/bin/env node
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+
6
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
7
+ const cliEntry = join(packageRoot, "dist", "cli.js");
8
+
9
+ if (!existsSync(cliEntry)) {
10
+ console.error(
11
+ `loop-agent: cannot find built CLI at ${cliEntry}. Run \`npm run build\` before using the package bin.`,
12
+ );
13
+ process.exit(1);
14
+ }
15
+
16
+ try {
17
+ await import(pathToFileURL(cliEntry).href);
18
+ } catch (error) {
19
+ console.error(error instanceof Error ? error.message : String(error));
20
+ process.exit(1);
21
+ }
@@ -141,7 +141,7 @@ async function runCursorPromptBatch(task, cwd, model, timeoutMs) {
141
141
  }
142
142
  async function runCursorPromptStreaming(task, cwd, model, timeoutMs) {
143
143
  const startedAt = Date.now();
144
- process.stderr.write(`[cursor-prompt] streaming (model=${model}, cwd=${cwd})
144
+ process.stderr.write(`[cursor-prompt] streaming (model=${model}, cwd=${cwd})
145
145
  `);
146
146
  const runDir = (await computeRunDir(cwd, task)) ?? undefined;
147
147
  const result = await executeCursorPromptStream({
@@ -164,15 +164,15 @@ async function runCursorPromptStreaming(task, cwd, model, timeoutMs) {
164
164
  });
165
165
  const elapsed = ((Date.now() - startedAt) / 1000).toFixed(1);
166
166
  if (result.ok) {
167
- process.stderr.write(`
168
- [cursor-prompt] done in ${elapsed}s, status=${result.status}
167
+ process.stderr.write(`
168
+ [cursor-prompt] done in ${elapsed}s, status=${result.status}
169
169
  `);
170
170
  }
171
171
  else {
172
172
  const stderr = result.stderr || "(no output)";
173
- process.stderr.write(`
174
- [cursor-prompt] FAILED in ${elapsed}s (${result.failureCategory}):
175
- ${stderr}
173
+ process.stderr.write(`
174
+ [cursor-prompt] FAILED in ${elapsed}s (${result.failureCategory}):
175
+ ${stderr}
176
176
  `);
177
177
  process.exit(1);
178
178
  }
@@ -4,7 +4,6 @@ import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { isDeepStrictEqual } from "node:util";
6
6
  import { fileURLToPath } from "node:url";
7
- import { copyDir } from "../shared/copy-dir.js";
8
7
  import { isInitRuntimeActive, } from "../shared/runtime-activity.js";
9
8
  import { loadHarnessManifest } from "../governance/harness.js";
10
9
  import { OPENCODE_TRANSIENT_RETRY_PLUGIN_PATH, applyPiRetryMerge, buildOpenCodeTransientRetryPluginSource, inspectPiRetryConfig, parseClientRecoveryMode, runClientRecovery, } from "./client-recovery.js";
@@ -119,6 +118,12 @@ const COMPAT_PROMPTS = {
119
118
  ].join("\n"),
120
119
  };
121
120
  const GOVERNANCE_ROOT_TOKEN = "__LOOP_AGENT_GOVERNANCE_ROOT__";
121
+ const PROJECT_NAME_TOKEN = "__LOOP_AGENT_PROJECT_NAME__";
122
+ /** Package-shipped render source for AGENTS.md managed block; not projected to target repos. */
123
+ const MANAGED_AGENTS_TEMPLATE_PATH = "docs/templates/init-managed-agents.md";
124
+ /** Files under package assets that init must not copy into target projects. */
125
+ const PACKAGE_ONLY_SURFACE_FILES = new Set([MANAGED_AGENTS_TEMPLATE_PATH]);
126
+ let managedAgentsTemplateCache;
122
127
  const INIT_CHECK_ENGINEERING_STRUCTURE_SH = `#!/usr/bin/env bash
123
128
  set -euo pipefail
124
129
 
@@ -798,130 +803,28 @@ function mergeRecord(base, patch) {
798
803
  ...patch,
799
804
  };
800
805
  }
801
- function buildManagedAgentsBlock(input) {
802
- return [
803
- MANAGED_BLOCK_START,
804
- "## loop-agent 治理",
805
- "",
806
- `本仓库已初始化为 \`${input.projectName}\` loop-agent harness 项目。`,
807
- "",
808
- "### 默认立场",
809
- "",
810
- "- 仓库是记录系统:决策、契约、计划、验证、报告和交接应进入可追踪文件。",
811
- "- 一次只推进一个有边界、可验证的工作块;实现前先搜索现有代码、文档、脚本和测试。",
812
- "- Shell 验证是完成依据;模型建议不能替代命令证据。",
813
- "- 保留无关的用户改动,不要回退自己没有做的修改。",
814
- "- 委托模型写入前,必须把写入边界写成结构化 `task.json.allowedPaths` / `task.json.forbiddenPaths`,再审查生成 DAG 的 writer `writeSet`;不要只依赖 `source/执行约束.md` 的自然语言约束。",
815
- "- 本仓库对 openCode 等主会话的定位是 **Compatibility / Operator Assist**:主会话编排 CLI 与只读诊断,**不是**默认实现 agent。",
816
- "",
817
- "### 主会话硬约束(Compatibility / Operator Assist)",
818
- "",
819
- "主会话(含 openCode、Cursor Chat、其他宿主 agent)= **operator-only**;skills 与本文件是纪律文档,**不能**替代 `task.json` / DAG `writeSet` / runtime 执法。",
820
- "",
821
- "| 类别 | 规则 |",
822
- "|---|---|",
823
- "| **允许** | 已发布 `loop-agent` / `agent-worker` CLI;只读 status/doctor/report/inspect/observe;准备 `source/*` 与 `task.json` 边界;human gate;shell 验证与 handoff。 |",
824
- "| **禁止** | 绕过 CLI 用宿主 Edit/Write/ApplyPatch 直接改业务实现;CLI/DAG 失败后「救火改文件」;用聊天自述代替 shell 验证。 |",
825
- "| **失败时** | `dag doctor` / `dag report` / `dag reconcile-run`(及适用 worker reconcile);修正任务源/`task.json`/DAG 后经 CLI 重跑。 |",
826
- "| **实现写入** | 业务代码 **只** 经受治理 DAG writer(`implement-pi` / `repair-pi`)经 `dag run-task` / `run-dag`。 |",
827
- "",
828
- "**永远不要**:`loop-agent` / `agent-worker` 失败 ⇒ 主会话直接改仓库实现。",
829
- "",
830
- "### 自然语言入口路由",
831
- "",
832
- "| 用户表达 | 入口 | 执行动作 |",
833
- "|---|---|---|",
834
- "| loop-agent 初始化 / loop agent 初始化 / loop agent初始化 / 初始化 loop-agent | 初始化 | 完成确定性初始化闭环 |",
835
- "| 初始化更新校验 / 检查初始化更新 / loop-agent 初始化更新校验 / loop agent初始化更新校验 | 更新校验 | 只读报告,不写入 |",
836
- "| 初始化对齐 / 升级后对齐 / reconcile 初始化 / loop-agent 初始化对齐 | 升级对齐 | 自动应用确定性安全动作;活跃 DAG/Worker 或人工决策时零写入 |",
837
- "| 初始化安全更新 / 应用初始化更新 / loop-agent 初始化安全更新 / loop agent初始化安全更新 | 安全更新 | 先 check-update,再只执行确定性安全动作 |",
838
- "| loop-agent 帮我完成 / 帮我实现 / 帮我修复 / 帮我开发 <需求>;使用 loop-agent 完成 <X>;按 loop-agent 流程处理 <X> | 通用需求实现 | 先建任务源与 `task.json.allowedPaths` / `forbiddenPaths`,再 `new-task` → `dag run-task` → `dag validate` → `run-dag`;主会话不得直接修改业务实现 |",
839
- "",
840
- "**更新校验(只读)**:只读执行下面命令;**不得自动**执行 `apply-safe` 或模型合并。",
841
- "",
842
- "```bash",
843
- "loop-agent init check-update --repo-root . --markdown",
844
- "```",
845
- "",
846
- "**升级对齐**:`loop-agent init reconcile --repo-root .`;存在活跃 DAG/Worker、human decisions 时必须零写入。",
847
- "",
848
- "**安全更新**:先 check-update,再 `apply-safe`;surface 缺失才 `--bootstrap-surface`。完成后复查 `init doctor` / `inspect` / `docs audit` 与目标治理检查。",
849
- "",
850
- "```bash",
851
- "loop-agent init check-update --repo-root . --markdown",
852
- "loop-agent init update --repo-root . --apply-safe",
853
- "```",
854
- "",
855
- "### 初始化闭环",
856
- "",
857
- `当用户要求初始化 loop-agent(“初始化 loop-agent”“loop agent 初始化”“loop agent初始化”“loop-agent 初始化”),或发现 \`README.md\` / \`${input.governanceRoot}/verification-matrix.md\` 仍停留在通用初始化状态时,模型应在同一轮工作中完成初始化闭环,不要把补全文档和验证矩阵留给用户交互。`,
858
- "",
859
- "1. 运行 `loop-agent init instructions --repo-root .`。",
860
- "2. 运行 `loop-agent init --repo-root . --profile full --merge`。",
861
- `3. 基于真实项目文件补全根 \`README.md\`、\`${input.governanceRoot}/verification-matrix.md\`,必要时适配 \`scripts/ci-tests.sh\`。`,
862
- "4. 复查 `init doctor` / `inspect` / `docs audit` 与 quick verification。",
863
- "",
864
- "信息不足时写明尚未识别到的事实;不要留下泛化占位符。",
865
- "",
866
- "### 文档收敛",
867
- "",
868
- "完成实现和验证后,必须检查 `README.md`、`AGENTS.md`、`CHANGELOG.md`(如果目标项目维护)、治理文档、skills references、初始化模板和脚本说明是否仍与实际行为一致。只更新与本次变更相关的内容;如果决定不更新,应在交接里写明理由。",
869
- "",
870
- "### 开始顺序",
871
- "",
872
- `pwd → \`README.md\` → \`harness.json\` → \`${input.governanceRoot}/README.md\` → 实现类再读 principles/feature-workflow/verification-matrix;测试纪律读 harness-methodology-*;\`git status\`;最小基线验证。`,
873
- "",
874
- "### Agent DAG 路径",
875
- "",
876
- "```bash",
877
- 'loop-agent new-task <task-id> "任务标题"',
878
- "# write .harness/tasks/<task-id>/source/需求.md",
879
- "# write .harness/tasks/<task-id>/source/执行约束.md",
880
- "loop-agent dag run-task <task-id> --profile auto --strict-models",
881
- "loop-agent dag validate --dag .harness/tasks/<task-id>/dag.json --strict-models --strict-governance",
882
- "loop-agent run-dag --dag .harness/tasks/<task-id>/dag.json --cwd .",
883
- "```",
884
- "",
885
- "`source/需求.md` 与 `source/执行约束.md` 必需;写入前同步 `task.json.allowedPaths` / `task.json.forbiddenPaths` 并审查 writer `writeSet`。",
886
- "",
887
- "凡是影响项目公共契约、执行入口、交付流水线、自动化/治理、数据模型、安全或权限模型、跨模块行为、用户可见工作流的改动,都必须在编辑实现文件前先创建任务、写好两个 source 文件、生成 DAG,并审查 DAG/writeSet。",
888
- "",
889
- "### 任务类型路由(taskKind)",
890
- "",
891
- '- 用户明确提出后端测试、接口/API 测试、pytest,或语境明确为后端的自动化测试时,必须把 `.harness/tasks/<task-id>/task.json` 的 `taskKind` 设置为 `"backend-test"`,不得保留默认 `standard`。',
892
- "- `backend-test` 是 `taskKind`,不是 `--profile` 的可选值;运行 `dag run-task` 时继续使用 `--profile auto`。",
893
- "- 仅出现“自动化测试”且无法判断前后端时,先阅读任务源与目标项目技术栈再决定,禁止无条件路由到 `backend-test`。",
894
- '- 用户提示词明确是前端实现需求(例如前端页面、UI、组件或交互开发)时,必须把 `.harness/tasks/<task-id>/task.json` 的 `taskKind` 设置为 `"frontend-implementation"`,不得保留默认 `standard`。',
895
- "- `frontend-implementation` 是 `taskKind`,不是 `--profile` 的可选值;运行 `dag run-task` 时继续使用 `--profile auto`,也可按需显式选择 `minimal` / `standard` / `reviewed` / `supervised`,不要把业务模板名当作 profile。",
896
- '- 前端自动化测试(浏览器/UI 自动化、Playwright、E2E)继续使用 `taskKind: "frontend-test"`,不得设置为 `frontend-implementation`。',
897
- "",
898
- "### 运行看板(只读)",
899
- "",
900
- "```bash",
901
- "agent-worker console # 默认 repo=当前目录,port=8790",
902
- "agent-worker console serve --repo . --port 8790 # 兼容入口,等价于上面裸入口",
903
- "```",
904
- "",
905
- "浏览器打开 `http://127.0.0.1:8790/`;检视面为 `http://127.0.0.1:8790/inspect/`。默认绑定本机 `127.0.0.1`;不要直接暴露到公开网络。端口被占用时不会自动更换,请用 `--port <port>` 显式指定。`agent-worker observe serve` 仅为兼容入口。",
906
- "",
907
- "### DAG 诊断与收口",
908
- "",
909
- "用 `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。",
910
- "",
911
- "恢复:doctor/report → classify → reconcile/replan → CLI 重跑 → shell verify。**禁止**把主会话直接 Edit 业务代码当作恢复手段。",
912
- "",
913
- "### 运行态与验证",
914
- "",
915
- "- `.harness/tasks/`、`.harness/dag-runs/`、`.harness/runs/` 保存运行事实;已完成事实只读。",
916
- "- `.agents/skills/` 为本地 skills;缺失时可回退 npm 包内置。",
917
- `- 验证命令选择:\`${input.governanceRoot}/verification-matrix.md\`。常用:\`bash scripts/check-repo.sh\`、\`bash scripts/ci.sh\`、\`loop-agent inspect\`、\`loop-agent doctor\`、\`loop-agent docs audit\`。`,
918
- "- `scripts/ci-tests.sh` 必须反映目标项目真实工具链。Windows 脚本用 Git Bash;仓库引用用 `/`。",
919
- "",
920
- "### 交接",
921
- "",
922
- `交接写清变更、原因、验证证据、影响面、风险与下一步;长期结论进入 \`${input.governanceRoot}/progress\`、reports、exec-plans、decisions。`,
923
- MANAGED_BLOCK_END,
924
- ].join("\n");
806
+ async function loadManagedAgentsTemplate(assetRoot) {
807
+ if (managedAgentsTemplateCache !== undefined)
808
+ return managedAgentsTemplateCache;
809
+ const templatePath = path.join(assetRoot, MANAGED_AGENTS_TEMPLATE_PATH);
810
+ const raw = await readFile(templatePath, "utf-8");
811
+ // Prefer a START marker on its own line so header notes mentioning the token are ignored.
812
+ const lines = raw.split(/\r?\n/);
813
+ const startLine = lines.findIndex((line) => line.trim() === MANAGED_BLOCK_START);
814
+ const endLine = startLine >= 0
815
+ ? lines.findIndex((line, index) => index > startLine && line.trim() === MANAGED_BLOCK_END)
816
+ : -1;
817
+ if (startLine < 0 || endLine < 0) {
818
+ throw new Error(`managed agents template missing LOOP_AGENT_INIT markers: ${MANAGED_AGENTS_TEMPLATE_PATH}`);
819
+ }
820
+ managedAgentsTemplateCache = lines.slice(startLine, endLine + 1).join("\n");
821
+ return managedAgentsTemplateCache;
822
+ }
823
+ async function buildManagedAgentsBlock(input) {
824
+ const template = await loadManagedAgentsTemplate(input.assetRoot);
825
+ return template
826
+ .replaceAll(PROJECT_NAME_TOKEN, input.projectName)
827
+ .replaceAll(GOVERNANCE_ROOT_TOKEN, input.governanceRoot);
925
828
  }
926
829
  function mergeManagedBlock(existing, block) {
927
830
  const start = existing.indexOf(MANAGED_BLOCK_START);
@@ -1137,11 +1040,39 @@ async function copyFileIfMissing(input) {
1137
1040
  async function copyDirMerge(input) {
1138
1041
  const source = path.join(input.assetRoot, input.sourceRelativePath);
1139
1042
  const target = path.join(input.repoRoot, input.targetRelativePath);
1140
- await copyDir(source, target);
1043
+ const sourcePrefix = `${input.sourceRelativePath.replaceAll(path.sep, "/").replace(/\/$/, "")}/`;
1044
+ await copyDirSkippingPackageOnly({
1045
+ source,
1046
+ target,
1047
+ packageRelativePrefix: sourcePrefix,
1048
+ });
1141
1049
  input.written.push(input.targetRelativePath.endsWith("/")
1142
1050
  ? input.targetRelativePath
1143
1051
  : `${input.targetRelativePath}/`);
1144
1052
  }
1053
+ /** Like copyDir, but skips package-only assets that must not land in target projects. */
1054
+ async function copyDirSkippingPackageOnly(input) {
1055
+ await mkdir(input.target, { recursive: true });
1056
+ const entries = await readdir(input.source, { withFileTypes: true });
1057
+ for (const entry of entries) {
1058
+ const srcPath = path.join(input.source, entry.name);
1059
+ const destPath = path.join(input.target, entry.name);
1060
+ const packageRelative = `${input.packageRelativePrefix}${entry.name}`;
1061
+ if (entry.isDirectory()) {
1062
+ await copyDirSkippingPackageOnly({
1063
+ source: srcPath,
1064
+ target: destPath,
1065
+ packageRelativePrefix: `${packageRelative}/`,
1066
+ });
1067
+ continue;
1068
+ }
1069
+ if (PACKAGE_ONLY_SURFACE_FILES.has(packageRelative))
1070
+ continue;
1071
+ if (entry.isFile() || entry.isSymbolicLink()) {
1072
+ await copyFile(srcPath, destPath);
1073
+ }
1074
+ }
1075
+ }
1145
1076
  async function ensureHarnessDirs(repoRoot, written) {
1146
1077
  const dirs = [
1147
1078
  ".harness/prompts",
@@ -1289,7 +1220,10 @@ async function discoverCopiedSurfaceFiles(assetRoot) {
1289
1220
  continue;
1290
1221
  const files = await listRelativeFiles(rootPath);
1291
1222
  for (const relativePath of files) {
1292
- discovered.push(`${prefix}${relativePath}`);
1223
+ const manifestPath = `${prefix}${relativePath}`;
1224
+ if (PACKAGE_ONLY_SURFACE_FILES.has(manifestPath))
1225
+ continue;
1226
+ discovered.push(manifestPath);
1293
1227
  }
1294
1228
  }
1295
1229
  return discovered.sort();
@@ -1335,7 +1269,8 @@ async function buildDesiredSurfaceContent(input) {
1335
1269
  }
1336
1270
  if (manifestPath === "AGENTS.md") {
1337
1271
  return {
1338
- content: buildManagedAgentsBlock({
1272
+ content: await buildManagedAgentsBlock({
1273
+ assetRoot: input.assetRoot,
1339
1274
  projectName: input.projectName,
1340
1275
  governanceRoot: input.governanceRoot,
1341
1276
  }),
@@ -2427,7 +2362,11 @@ export async function initializeLoopAgentProject(options) {
2427
2362
  await writeText({
2428
2363
  repoRoot,
2429
2364
  relativePath: "AGENTS.md",
2430
- content: mergeManagedBlock(existingAgents, buildManagedAgentsBlock({ projectName, governanceRoot })),
2365
+ content: mergeManagedBlock(existingAgents, await buildManagedAgentsBlock({
2366
+ assetRoot,
2367
+ projectName,
2368
+ governanceRoot,
2369
+ })),
2431
2370
  merge: true,
2432
2371
  written,
2433
2372
  skipped,
@@ -37,17 +37,17 @@ export function parseLoopBenchmarkArgs(args) {
37
37
  return { json, markdown, outputPath };
38
38
  }
39
39
  export function printLoopBenchmarkUsage() {
40
- console.log(`usage: loop-benchmark [options]
41
-
42
- Deterministic loop-agent loop benchmark baseline (no live Pi/Cursor calls).
43
-
44
- Options:
45
- --json Emit JSON (default when no format flag is set)
46
- --markdown Emit Markdown report
47
- --output <path> Write Markdown report to a repo-relative or absolute path
48
- -h, --help Show this help
49
-
50
- Control groups: single-repair, 3-pass-convergence, 3-pass-convergence+quota.
40
+ console.log(`usage: loop-benchmark [options]
41
+
42
+ Deterministic loop-agent loop benchmark baseline (no live Pi/Cursor calls).
43
+
44
+ Options:
45
+ --json Emit JSON (default when no format flag is set)
46
+ --markdown Emit Markdown report
47
+ --output <path> Write Markdown report to a repo-relative or absolute path
48
+ -h, --help Show this help
49
+
50
+ Control groups: single-repair, 3-pass-convergence, 3-pass-convergence+quota.
51
51
  Recommendation never changes convergence.enabled default.`);
52
52
  }
53
53
  export async function runLoopBenchmark(repoRoot, rawArgs) {
@@ -106,22 +106,22 @@ export function parsePiReuseBenchmarkArgs(args) {
106
106
  };
107
107
  }
108
108
  export function printPiReuseBenchmarkUsage() {
109
- console.log(`usage: pi-reuse-benchmark [options]
110
-
111
- Deterministic Pi runtime reuse benchmark/decision summary (no live Pi calls).
112
-
113
- Options:
114
- --report <path> Benchmark report markdown (approval status)
115
- --approval <path> Explicit approval JSON artifact
116
- --off-executor <path> Baseline executor.jsonl (reuse off)
117
- --on-executor <path> Treatment executor.jsonl (reuse on)
118
- --off-task <task-id> Resolve baseline from .harness/tasks/<id>/logs/executor.jsonl
119
- --on-task <task-id> Resolve treatment from .harness/tasks/<id>/logs/executor.jsonl
120
- --json Emit JSON (default when no format flag is set)
121
- --markdown Emit Markdown summary
122
- -h, --help Show this help
123
-
124
- Recommendations: defer | maintain-opt-in | eligible-for-human-review
109
+ console.log(`usage: pi-reuse-benchmark [options]
110
+
111
+ Deterministic Pi runtime reuse benchmark/decision summary (no live Pi calls).
112
+
113
+ Options:
114
+ --report <path> Benchmark report markdown (approval status)
115
+ --approval <path> Explicit approval JSON artifact
116
+ --off-executor <path> Baseline executor.jsonl (reuse off)
117
+ --on-executor <path> Treatment executor.jsonl (reuse on)
118
+ --off-task <task-id> Resolve baseline from .harness/tasks/<id>/logs/executor.jsonl
119
+ --on-task <task-id> Resolve treatment from .harness/tasks/<id>/logs/executor.jsonl
120
+ --json Emit JSON (default when no format flag is set)
121
+ --markdown Emit Markdown summary
122
+ -h, --help Show this help
123
+
124
+ Recommendations: defer | maintain-opt-in | eligible-for-human-review
125
125
  Never changes CODE_AGENT_PI_REUSE_RUNTIME default (off).`);
126
126
  }
127
127
  function resolveRepoRelative(repoRoot, filePath) {
@@ -169,14 +169,25 @@ export function buildDagPiUserMessage(task, persona, step) {
169
169
  "Do not wrap the output in code fences and do not add conversational preamble.",
170
170
  ].join(" ");
171
171
  }
172
- function resolveDagPiModelConfig(model) {
173
- const provider = DAG_PI_MODEL_PROVIDERS[model] ?? DEFAULT_DAG_PI_PROVIDER;
172
+ export function resolveDagPiModelConfig(modelReference, options) {
173
+ const separatorIndex = modelReference.indexOf("/");
174
+ const qualified = separatorIndex >= 0;
175
+ const provider = qualified
176
+ ? modelReference.slice(0, separatorIndex)
177
+ : (DAG_PI_MODEL_PROVIDERS[modelReference] ?? DEFAULT_DAG_PI_PROVIDER);
178
+ const model = qualified
179
+ ? modelReference.slice(separatorIndex + 1)
180
+ : modelReference;
181
+ if (!provider || !model) {
182
+ throw new Error(`invalid DAG Pi model reference "${modelReference}": expected non-empty provider/model`);
183
+ }
184
+ const explicitThinking = options?.thinking?.trim();
185
+ const thinking = explicitThinking ??
186
+ (provider === "wizard-local" && model === "gpt-5.5" ? "low" : undefined);
174
187
  return {
175
188
  provider,
176
189
  model,
177
- ...(provider === "wizard-local" && model === "gpt-5.5"
178
- ? { thinking: "low" }
179
- : {}),
190
+ ...(thinking ? { thinking } : {}),
180
191
  };
181
192
  }
182
193
  const SUMMARY_STDOUT_MAX = 4_000;
@@ -291,7 +302,7 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep) {
291
302
  : undefined;
292
303
  const result = await piStepFn({
293
304
  attachedFiles: [],
294
- modelConfig: resolveDagPiModelConfig(input.model),
305
+ modelConfig: resolveDagPiModelConfig(input.model, input.thinking ? { thinking: input.thinking } : undefined),
295
306
  prompt: input.prompt,
296
307
  repoRoot: input.cwd,
297
308
  step,
@@ -304,8 +315,8 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep) {
304
315
  abortGraceMs: input.abortGraceMs,
305
316
  onActivity: bridgeActivity
306
317
  ? (activity) => {
307
- if (activity.kind === "lease"
308
- || activity.kind === "synthetic-heartbeat") {
318
+ if (activity.kind === "lease" ||
319
+ activity.kind === "synthetic-heartbeat") {
309
320
  return;
310
321
  }
311
322
  bridgeActivity(activity.kind, activity.at);
@@ -372,7 +383,9 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep) {
372
383
  }
373
384
  }
374
385
  }
375
- if (writeGuardOk && beforeStatus !== undefined && changeManifestChangedFiles !== undefined) {
386
+ if (writeGuardOk &&
387
+ beforeStatus !== undefined &&
388
+ changeManifestChangedFiles !== undefined) {
376
389
  await persistWriterChangeManifest({
377
390
  runDir: meta.runDir,
378
391
  nodeId: input.task.id,
@@ -445,12 +458,8 @@ function parseWriterImplementationOutcome(text) {
445
458
  const normalized = normalizeProtocolLine(line, WRITER_OUTCOME_PROTOCOL_LINE, nextLine);
446
459
  if (normalized === undefined)
447
460
  continue;
448
- const value = normalized
449
- .slice(WRITER_OUTCOME_PROTOCOL_LINE.length)
450
- .trim();
451
- const outcome = isWriterImplementationOutcome(value)
452
- ? value
453
- : undefined;
461
+ const value = normalized.slice(WRITER_OUTCOME_PROTOCOL_LINE.length).trim();
462
+ const outcome = isWriterImplementationOutcome(value) ? value : undefined;
454
463
  candidates.push({
455
464
  lineIndex,
456
465
  value,
@@ -474,9 +483,7 @@ function parseWriterImplementationOutcome(text) {
474
483
  };
475
484
  }
476
485
  function isWriterImplementationOutcome(value) {
477
- return (value === "changed" ||
478
- value === "already-satisfied" ||
479
- value === "blocked");
486
+ return (value === "changed" || value === "already-satisfied" || value === "blocked");
480
487
  }
481
488
  function writerOutcomeDiagnostics(text, parsed, changedFiles) {
482
489
  const firstNonEmpty = text
@@ -543,8 +550,7 @@ function canonicalizeProtocolFirstLine(assistantText, firstProtocolLine) {
543
550
  if (protocolNextLineIndex === protocolIndex + 1) {
544
551
  after.shift();
545
552
  }
546
- while (before.at(-1)?.trim() === "" &&
547
- after.at(0)?.trim() === "") {
553
+ while (before.at(-1)?.trim() === "" && after.at(0)?.trim() === "") {
548
554
  after.shift();
549
555
  }
550
556
  const bodyLines = [...before, ...after];
@@ -1,4 +1,5 @@
1
- import { DEFAULT_DAG_EXECUTOR_MODELS, } from '../workflows/dag/types.js';
1
+ import { DEFAULT_DAG_EXECUTOR_MODELS, } from "../workflows/dag/types.js";
2
+ import { normalizeExecutorTierValue, } from "../governance/manifest-types.js";
2
3
  export const DEFAULT_DAG_MODELS = {
3
4
  HIGH: "gpt-5.5",
4
5
  MED: "gpt-5.5",
@@ -8,6 +9,16 @@ export const DEFAULT_DAG_MODELS = {
8
9
  * DAG executor model tier keys that may carry a per-complexity override.
9
10
  */
10
11
  const EXECUTOR_MODEL_TIERS = ["LOW", "MED", "HIGH"];
12
+ /** Resolve one tier to a model and optional explicit thinking override. */
13
+ export function resolveExecutorTierSelection(execConfig, tier) {
14
+ const tierSelection = normalizeExecutorTierValue(execConfig?.[tier]);
15
+ if (tierSelection)
16
+ return tierSelection;
17
+ const defaultModel = execConfig?.defaultModel;
18
+ if (defaultModel && defaultModel !== "default")
19
+ return { model: defaultModel };
20
+ return { model: DEFAULT_DAG_EXECUTOR_MODELS.pi[tier] };
21
+ }
11
22
  /**
12
23
  * Resolve the DAG executor model matrix for Pi from harness `executors.pi`.
13
24
  *
@@ -19,22 +30,23 @@ const EXECUTOR_MODEL_TIERS = ["LOW", "MED", "HIGH"];
19
30
  * The "default" literal (injected by the schema `.default("default")`) and
20
31
  * absent/undefined both mean "no override, fall through".
21
32
  */
22
- export function resolveExecutorModelMatrix(executor, execConfig) {
23
- const tierValue = (tier) => {
24
- const tierOverride = execConfig?.[tier];
25
- if (tierOverride && tierOverride !== "default")
26
- return tierOverride;
27
- const defaultModel = execConfig?.defaultModel;
28
- if (defaultModel && defaultModel !== "default")
29
- return defaultModel;
30
- return DEFAULT_DAG_EXECUTOR_MODELS[executor][tier];
31
- };
33
+ export function resolveExecutorModelMatrix(_executor, execConfig) {
32
34
  return {
33
- LOW: tierValue("LOW"),
34
- MED: tierValue("MED"),
35
- HIGH: tierValue("HIGH"),
35
+ LOW: resolveExecutorTierSelection(execConfig, "LOW").model,
36
+ MED: resolveExecutorTierSelection(execConfig, "MED").model,
37
+ HIGH: resolveExecutorTierSelection(execConfig, "HIGH").model,
36
38
  };
37
39
  }
40
+ /** Resolve only explicitly configured per-tier thinking values. */
41
+ export function resolveExecutorThinkingMatrix(execConfig) {
42
+ const result = {};
43
+ for (const tier of EXECUTOR_MODEL_TIERS) {
44
+ const selection = normalizeExecutorTierValue(execConfig?.[tier]);
45
+ if (selection?.thinking)
46
+ result[tier] = selection.thinking;
47
+ }
48
+ return result;
49
+ }
38
50
  /**
39
51
  * Resolve the Pi DAG executor model matrix from a harness manifest.
40
52
  */
@@ -55,7 +67,9 @@ export function resolveModelSelection(manifest, taskConfig, step, options) {
55
67
  };
56
68
  }
57
69
  const profileName = resolveProfileName(manifest, taskConfig.complexity, step, retryAttempt);
58
- const profile = profileName ? manifest.modelProfiles?.[profileName] : undefined;
70
+ const profile = profileName
71
+ ? manifest.modelProfiles?.[profileName]
72
+ : undefined;
59
73
  if (!profile) {
60
74
  return {
61
75
  modelConfig: manifest.models?.[step],
@@ -74,7 +88,9 @@ export function resolveModelSelection(manifest, taskConfig, step, options) {
74
88
  };
75
89
  }
76
90
  function resolveProfileName(manifest, complexity, step, retryAttempt) {
77
- if (step === 'implement' && retryAttempt > 0 && manifest.modelRouting?.implementRetry) {
91
+ if (step === "implement" &&
92
+ retryAttempt > 0 &&
93
+ manifest.modelRouting?.implementRetry) {
78
94
  return manifest.modelRouting.implementRetry;
79
95
  }
80
96
  const route = manifest.modelRouting?.[step];
@@ -85,11 +101,11 @@ function resolveProfileName(manifest, complexity, step, retryAttempt) {
85
101
  }
86
102
  export function formatModelSelectionLabel(modelConfig, profileName) {
87
103
  if (!modelConfig) {
88
- return profileName ? `${profileName}` : 'default';
104
+ return profileName ? `${profileName}` : "default";
89
105
  }
90
106
  const base = modelConfig.provider && modelConfig.model
91
107
  ? `${modelConfig.provider}/${modelConfig.model}`
92
- : modelConfig.model ?? 'default';
108
+ : (modelConfig.model ?? "default");
93
109
  return profileName ? `${profileName}:${base}` : base;
94
110
  }
95
111
  export function formatFallbackLabel(profile) {
@@ -539,7 +539,7 @@ async function executeBackendTestPipeline(input, meta) {
539
539
  });
540
540
  const manifestPath = path.join(meta.runDir, "contracts", "backend-test-case-manifest.json");
541
541
  const summary = manifest.coverageSummary;
542
- outputs.push(`manifest=${manifestPath}`, `materializationStatus=${manifest.materializationStatus ?? "available"}`, `coverageSummary.explicitAcCount=${summary?.explicitAcCount ?? "unavailable"}`, `coverageSummary.coveredAcCount=${summary?.coveredAcCount ?? "unavailable"}`, `coverageSummary.caseCount=${summary?.caseCount ?? "unavailable"}`, `coverageSummary.generatedCount=${summary?.generatedCount ?? "unavailable"}`, `ruleCoverageSummary.ruleCount=${manifest.ruleCoverageSummary?.ruleCount ?? "unavailable"}`, `correspondenceSummary.exactCorrespondenceCount=${manifest.correspondenceSummary?.exactCorrespondenceCount ?? "unavailable"}`);
542
+ outputs.push(`manifest=${manifestPath}`, `materializationStatus=${manifest.materializationStatus ?? "available"}`, `coverageSummary.explicitAcCount=${summary?.explicitAcCount ?? "unavailable"}`, `coverageSummary.coveredAcCount=${summary?.coveredAcCount ?? "unavailable"}`, `coverageSummary.caseCount=${summary?.caseCount ?? "unavailable"}`, `coverageSummary.generatedCount=${summary?.generatedCount ?? "unavailable"}`, `ruleCoverageSummary.ruleCount=${manifest.ruleCoverageSummary?.ruleCount ?? "unavailable"}`, `correspondenceSummary.exactCorrespondenceCount=${manifest.correspondenceSummary?.exactCorrespondenceCount ?? "unavailable"}`, `correspondenceSummary.primarySymbolCount=${manifest.correspondenceSummary?.primarySymbolCount ?? "unavailable"}`, `correspondenceSummary.testPoints=${manifest.correspondenceSummary?.mappedTestPointCount ?? "unavailable"}/${manifest.correspondenceSummary?.testPointCount ?? "unavailable"}`, `correspondenceSummary.variantTestPointCount=${manifest.correspondenceSummary?.variantTestPointCount ?? "unavailable"}`, `correspondenceSummary.assertionTestPointCount=${manifest.correspondenceSummary?.assertionTestPointCount ?? "unavailable"}`, `correspondenceSummary.crossCuttingTestPointCount=${manifest.correspondenceSummary?.crossCuttingTestPointCount ?? "unavailable"}`, `correspondenceSummary.unclassifiedTestPointCount=${manifest.correspondenceSummary?.unclassifiedTestPointCount ?? "unavailable"}`, `correspondenceSummary.duplicateBindingTestPointCount=${manifest.correspondenceSummary?.duplicateBindingTestPointCount ?? "unavailable"}`);
543
543
  }
544
544
  else if (pipeline === "markdown-execute-html") {
545
545
  const mappedScripts = await collectBackendTestMappedPytestScripts(input.cwd);