@tea-agent/loop-agent 0.40.0-next.5 → 0.40.0-next.7

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 (75) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/dist/build-stamp.json +3 -3
  3. package/dist/executors/dag-pi-executor.js +20 -2
  4. package/dist/executors/pi-writer-tool-policy.js +57 -1
  5. package/dist/executors/shell-executor.js +14 -2
  6. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CGZrpj0K.js → abnfDiagram-N423BO3Z-DGt5TRsk.js} +1 -1
  7. package/dist/worker/console/static/assets/{arc-CwEyA8iq.js → arc-DKBSJsXm.js} +1 -1
  8. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DLTkrEQ3.js → architectureDiagram-T3A2C74G-BnhooRBs.js} +1 -1
  9. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-4w12zR3t.js → blockDiagram-VBNYF7ZC-Bm9qkrJP.js} +1 -1
  10. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BppqLAjk.js → c4Diagram-5PPSVZJV-B2cFh4bO.js} +1 -1
  11. package/dist/worker/console/static/assets/channel-DSgFX07x.js +1 -0
  12. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DO6U26Pm.js → chunk-2GRJ4B5K-DhvWRI03.js} +1 -1
  13. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DC7YEdOy.js → chunk-2Q5K7J3B-chzTP_2w.js} +1 -1
  14. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DtMYVQBw.js → chunk-5RXB4S5H-SdWIR12m.js} +1 -1
  15. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C5Ed-57N.js → chunk-5VM5RSS4-C2-8Eo_A.js} +1 -1
  16. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BUY4oIzT.js → chunk-6Q2QTUOP-DvbE1yQW.js} +1 -1
  17. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-VnWHALIV.js → chunk-GF5L2VYU-CEenyPTz.js} +1 -1
  18. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-COV-RQk4.js → chunk-JWPE2WC7-CcaZrq0k.js} +1 -1
  19. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-dSnLtUiz.js → chunk-KBJHAD2P-FGngOTto.js} +1 -1
  20. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CX6FDzWh.js → chunk-RYQCIY6F-jT_yX3sf.js} +1 -1
  21. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-D3mEgb6W.js → chunk-XXDRQBXY-CjE9LQOM.js} +1 -1
  22. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-unJObRls.js +1 -0
  23. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-unJObRls.js +1 -0
  24. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-QQBMUnAf.js → cose-bilkent-JH36ORCC-GQWns1tb.js} +1 -1
  25. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BxSD-LVV.js → cynefin-VYW2F7L2-CGRpxWNJ.js} +1 -1
  26. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DxHNiNbk.js → cynefinDiagram-MW4NZA55-Cd3uFGE4.js} +1 -1
  27. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C83bYy8E.js → dagre-VZM6K2ZE-DmDHuUW0.js} +1 -1
  28. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BhtZu2YY.js → diagram-7IWD3JNH-kAr--hF3.js} +1 -1
  29. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-K0P_ntRE.js → diagram-B4RE2ZJO-woahL2Oh.js} +1 -1
  30. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CXYy1Nib.js → diagram-LBJQPF4R-7syNVaNf.js} +1 -1
  31. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D-CtIFkU.js → diagram-Q27KOJAE-BELPXiaY.js} +1 -1
  32. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BEK9-_7c.js → diagram-UB23O5K3-CrPa4aXi.js} +1 -1
  33. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-L0EX-9wn.js → ebnfDiagram-BXEA7PRR-B48u2VVe.js} +1 -1
  34. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-9pt0fc_5.js → erDiagram-JOGREHBK-DhBTfwTF.js} +1 -1
  35. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CZVeM_n1.js → flowDiagram-UKHOOZJN-CAeZ5nDU.js} +1 -1
  36. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DqUKkAZP.js → ganttDiagram-PKOTCBZU-CkIFeFLN.js} +1 -1
  37. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-BYUuXEgp.js → gitGraphDiagram-DS77QQ5N-t0HGYDel.js} +1 -1
  38. package/dist/worker/console/static/assets/{index-B0jGzQj6.js → index-B4l5ag79.js} +32 -32
  39. package/dist/worker/console/static/assets/index-CwVHWB71.css +1 -0
  40. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DNzKk7ah.js → infoDiagram-6WML65LV-WM0HZhe4.js} +1 -1
  41. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-Clbyg5IP.js → ishikawaDiagram-WSZJBQD7-CV5byHe0.js} +1 -1
  42. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-zDO_gZNO.js → journeyDiagram-NVQOT4AX-lIyWkgnw.js} +1 -1
  43. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-x3LpRYek.js → kanban-definition-27J2QSJJ-BIH4YNRY.js} +1 -1
  44. package/dist/worker/console/static/assets/{linear-CTpwZqKR.js → linear-lQ0ZGrWc.js} +1 -1
  45. package/dist/worker/console/static/assets/{mermaid.core-D_WXK6C7.js → mermaid.core-BYdXtke8.js} +5 -5
  46. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CWK-OYP5.js → mindmap-definition-FAOFIHXS-k93vkIa4.js} +1 -1
  47. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-B9gtUPsM.js → pegDiagram-VL7TDLO6-Bm6AqvSy.js} +1 -1
  48. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-Dj2lzceR.js → pieDiagram-7S7Q4E2Y-DnJW6Xpf.js} +1 -1
  49. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Nwljut7m.js → quadrantDiagram-CIZ2JOQS-CMzm4ObL.js} +1 -1
  50. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BY-KNt93.js → railroadDiagram-AXF67PYL-fwFF5dFQ.js} +1 -1
  51. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-Vx0wvpPy.js → requirementDiagram-LRYGKXZP-tzl9VDUc.js} +1 -1
  52. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B6grNXUk.js → sankeyDiagram-W5VNT64P-B26V09DD.js} +1 -1
  53. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CteidBvh.js → sequenceDiagram-SI44F4Z6-D2npyCeP.js} +1 -1
  54. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Du4W8mFK.js → sizeCapture-X5ZJPWSS-CTJtkRU2.js} +1 -1
  55. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-jyI6Yh81.js → stateDiagram-OKZ733FA-B8lk4Lzu.js} +1 -1
  56. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BmOcMTJE.js +1 -0
  57. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DYWs_fRv.js → swimlanes-SLNWSIFB-C3yP53hH.js} +2 -2
  58. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Beah-dq0.js +8 -0
  59. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DO5LLy47.js → timeline-definition-Z64GVDOM-Bck7xjhe.js} +1 -1
  60. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CLF2p1_L.js → vennDiagram-T6HMQDX7-CCntr1w7.js} +1 -1
  61. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-AwTaYBxA.js → wardleyDiagram-T6FBY63Y-DsCqCBZz.js} +1 -1
  62. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DzbblQRw.js → xychartDiagram-ELKLHX3M-Br2l90OM.js} +1 -1
  63. package/dist/worker/console/static/index.html +2 -2
  64. package/dist/workflows/dag/backend-test-pytest-collection.js +11 -10
  65. package/dist/workflows/dag/dynamic-runtime/map.js +1 -0
  66. package/dist/workflows/dag/init-hybrid.js +66 -42
  67. package/dist/workflows/dag/types.js +4 -0
  68. package/docs/templates/backend-test-dag.json +246 -235
  69. package/package.json +1 -1
  70. package/dist/worker/console/static/assets/channel-TzuwQha5.js +0 -1
  71. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DCu69b3S.js +0 -1
  72. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DCu69b3S.js +0 -1
  73. package/dist/worker/console/static/assets/index-BiYbPiTw.css +0 -1
  74. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyQblISl.js +0 -1
  75. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DiygmgYP.js +0 -8
package/CHANGELOG.md CHANGED
@@ -4,7 +4,8 @@
4
4
 
5
5
  ### 重点更新
6
6
 
7
- - Operator Chat 多次输入排队对齐 DSH Web:运行中的连续输入改为持久化 FIFO 队列,不再用后一条覆盖前一条;单条直接展示,多条默认折叠为数量栏,展开后逐行提供内联编辑、删除和插话发送。编辑/删除以消息 ID + revision 做并发保护,插话复用 Pi `steer` 注入当前回合,其余消息继续按顺序在回合结束后自动发送;队列通过会话快照与 SSE 权威数组恢复,刷新或 Console 重启不丢失。
7
+ - backend-test Markdown plan 节点收紧为严格 readSet:仅读取绑定任务源、权威 reference 文档和 N1 环境报告,Pi 只暴露受策略包裹的 `read` 工具,禁止全仓库扫描;plan 不再写入 `testcase/md/README.md`,而是保存为 run-owned Harness artifact `generate-backend-md-plan-pi/plan.md`,N3 从该 artifact 提取模块并将 `planReadPath` 传递给下游 map writer。Markdown map child 与唯一 review/sync writer 也增加机器级 strict readSet:动态 child readSet 随 item 模板物化,strict writer 只获得 `read/edit/write`,不能通过 `grep/find/ls/bash` 递归发现仓库;edit 的预读取同样受 readSet 约束,review 机器禁止写 `testcase/md/README.md`。保持 16 节点拓扑不变;backend-test 定向回归通过。
8
+ - Operator Chat 多次输入排队对齐 DSH Web:运行中的连续输入改为持久化 FIFO 队列,不再用后一条覆盖前一条;单条直接展示,多条默认折叠为数量栏,展开后逐行提供内联编辑、删除和插话发送。队列 Dock 使用与 DSH 一致的外层定位与内层面板结构,标题固定、仅消息列表滚动,并在编辑或请求期间锁定折叠操作。编辑/删除以消息 ID + revision 做并发保护,插话复用 Pi `steer` 注入当前回合,其余消息继续按顺序在回合结束后自动发送;队列通过会话快照与 SSE 权威数组恢复,刷新或 Console 重启不丢失。
8
9
  - Operator Chat 右侧辅助栏统一上下文与调用记录:输入框的上下文环、调用记录入口现在复用文件/DAG 侧栏;「文件」常驻,「上下文 / 调用记录」按入口临时添加并支持独立 `×` 关闭,关闭后回到文件 Tab;点击已选入口可收起侧栏,切换到上下文时同步开启注入状态,收起时仅关闭注入而保留上下文数据。上下文与工具调用内容改为内嵌布局,不再叠加独立弹窗、遮罩、焦点陷阱或重复关闭按钮,并补充 Tab 切换与侧栏开合动画;修复折叠态右侧展开按钮无法恢复侧栏的问题。
9
10
  - Operator Chat 手动 compact 消息流对齐 DSH Web:压缩中在时间线显示状态行,完成后显示默认折叠的 `compact` 摘要边界,点击可展开完整 Markdown 压缩结果;compact 事件通过 SSE 与 session 详情快照双路径恢复,使用事件 ID 去重,刷新/重新进入会话不丢失。Pi compaction 会把摘要写入 session context,后续模型请求继续使用压缩后的上下文。
10
11
  - Operator Chat 对齐 DSH Web Todo 能力:新增 `todo_write` 整表替换工具与内容校验,Todo 状态通过会话事件持久化并支持刷新恢复;新一轮开始自动清空,输入框上方展示默认折叠的任务面板(待处理虚线圆环、进行中旋转圆环与完成勾选),消息流使用专用“更新任务清单”摘要行;手动压缩上下文时会把当前完整 Todo 列表合并进压缩指令,避免摘要遗漏仍在执行的任务。
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.40.0-next.5",
4
- "gitSha": "b18771d5844f30b886253fce8788f2774a6bcd5d",
5
- "builtAt": "2026-08-27T00:26:45.125Z"
3
+ "version": "0.40.0-next.7",
4
+ "gitSha": "59deccb31726ce014324385bd26a5558c5ed7a49",
5
+ "builtAt": "2026-08-27T14:04:40.295Z"
6
6
  }
@@ -4,7 +4,7 @@ import { readFile } from "node:fs/promises";
4
4
  import { writeDagNodeJsonArtifact, writeTextArtifactFile, } from "../infrastructure/harness/artifact-store.js";
5
5
  import { executePiStep, resolvePiBackend, } from "./pi-executor.js";
6
6
  import { resolveDagPiExtensions, } from "./pi-extension-resolver.js";
7
- import { buildPiWriterToolPolicyContext, createPiWriterCustomTools, } from "./pi-writer-tool-policy.js";
7
+ import { buildPiWriterToolPolicyContext, createPiReaderCustomTools, createPiWriterCustomTools, } from "./pi-writer-tool-policy.js";
8
8
  import { cleanupPlaywrightCliDefaultSession, createPlaywrightCliTool, PI_COMMAND_CAPABILITY_REGISTRY, resolveCaseIdFromWriteSet, resolveEvidenceDirFromWriteSet, } from "./pi-playwright-cli-tool.js";
9
9
  import { dagCommandPolicyAllows, resolveDagCommandPolicy, } from "../workflows/dag/types.js";
10
10
  import { redactPromptForLog, truncateOutput, } from "../shared/output-truncation.js";
@@ -182,6 +182,9 @@ export function resolveDagPiStepName(task) {
182
182
  return resolvePiExecutorStep(resolveDagPiPersona(task));
183
183
  }
184
184
  export function resolveDagPiToolNames(task) {
185
+ if ((task.readSet?.length ?? 0) > 0) {
186
+ return isDagPiWriteTask(task) ? ["read", "edit", "write"] : ["read"];
187
+ }
185
188
  if (!isDagPiWriteTask(task))
186
189
  return [...DAG_PI_READONLY_TOOLS];
187
190
  const tools = [...DAG_PI_WRITE_TOOLS];
@@ -238,6 +241,9 @@ export function buildDagPiUserMessage(task, persona, step) {
238
241
  `You are executing hybrid DAG node "${task.id}" (role=${role}, piPersona=${persona}, piStep=${step}, writePolicy=${writePolicy}).`,
239
242
  "The system prompt contains the full DAG envelope: objective, constraints, upstream context, and task.",
240
243
  "Use read-only tools only. Do not edit, write, or commit repository files.",
244
+ (task.readSet?.length ?? 0) > 0
245
+ ? `Strict read set: ${task.readSet.join(", ")}. The read tool rejects every other repository path; do not search for substitutes.`
246
+ : undefined,
241
247
  "Return your conclusion as plain Markdown text suitable for downstream DAG nodes.",
242
248
  "Do not wrap the output in code fences and do not add conversational preamble.",
243
249
  ].join(" ");
@@ -513,9 +519,17 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
513
519
  ok: false, stdout: "", stderr: "pi command capability requires an explicit bounded write tool profile", failureCategory: "tool-policy", durationMs: Date.now() - started,
514
520
  };
515
521
  }
516
- if (isWriteTask || allowsPlaywrightCli) {
522
+ const strictReadSet = (input.task.readSet?.length ?? 0) > 0;
523
+ if (isWriteTask || allowsPlaywrightCli || strictReadSet) {
517
524
  try {
518
525
  const customTools = [];
526
+ if (strictReadSet) {
527
+ const readContext = buildPiWriterToolPolicyContext({
528
+ repoRoot: input.cwd,
529
+ readSet: input.task.readSet,
530
+ });
531
+ customTools.push(...(await createPiReaderCustomTools(readContext)));
532
+ }
519
533
  if (isWriteTask) {
520
534
  const policyContext = buildPiWriterToolPolicyContext({
521
535
  repoRoot: input.cwd,
@@ -523,6 +537,7 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
523
537
  writeSet: input.task.writeSet,
524
538
  forbiddenPaths: input.task.forbiddenPaths,
525
539
  writePolicy: input.task.writePolicy,
540
+ readSet: input.task.readSet,
526
541
  });
527
542
  customTools.push(...(await createPiWriterCustomTools(policyContext)));
528
543
  }
@@ -688,6 +703,9 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
688
703
  persona,
689
704
  step,
690
705
  });
706
+ if (input.task.id === "generate-backend-md-plan-pi" && result.assistantText?.trim()) {
707
+ await writeTextArtifactFile(path.join(meta.runDir, input.task.id, "plan.md"), result.assistantText.trim() + "\n");
708
+ }
691
709
  const mapped = mapPiResultToDagNodeResult(result, input.task.writerOutcomePolicy
692
710
  ? WRITER_OUTCOME_PROTOCOL_LINE
693
711
  : input.task.firstProtocolLine);
@@ -154,6 +154,28 @@ function assertMkdirRelative(relative, ctx) {
154
154
  throw new PiWriterPathDeniedError("forbidden", relative || ".");
155
155
  }
156
156
  }
157
+ export async function assertReadablePath(targetPath, ctx) {
158
+ const raw = String(targetPath ?? "").trim();
159
+ if (!raw)
160
+ throw new PiWriterPathDeniedError("empty-path", targetPath ?? "");
161
+ const repoRoot = path.resolve(ctx.repoRoot);
162
+ const absoluteTarget = path.isAbsolute(raw)
163
+ ? path.resolve(raw)
164
+ : path.resolve(repoRoot, raw);
165
+ if (!isPathInside(repoRoot, absoluteTarget)) {
166
+ throw new PiWriterPathDeniedError("outside-repo", raw);
167
+ }
168
+ const effective = await resolveEffectiveMutationTarget(repoRoot, absoluteTarget);
169
+ const relative = repoRelativePath(repoRoot, absoluteTarget, raw, false);
170
+ const effectiveRelative = repoRelativePath(effective.realRoot, effective.absoluteTarget, raw, false);
171
+ const readSet = ctx.readSet ?? [];
172
+ for (const candidate of [relative, effectiveRelative]) {
173
+ if (readSet.length === 0 || !matchesAnyPattern(candidate, readSet)) {
174
+ throw new PiWriterPathDeniedError("read-outside-read-set", candidate, "path is outside the node readSet");
175
+ }
176
+ }
177
+ return relative;
178
+ }
157
179
  /**
158
180
  * Assert a mutation target is writable under the DAG writer policy.
159
181
  * Returns the normalized lexical repo-relative path on success.
@@ -201,11 +223,30 @@ export async function assertMkdirPath(dirPath, ctx) {
201
223
  assertMkdirRelative(effectiveRelative, ctx);
202
224
  return lexicalRelative;
203
225
  }
226
+ export function createPiReaderFsOperations(ctx) {
227
+ return {
228
+ readFile: async (absolutePath) => {
229
+ await assertReadablePath(absolutePath, ctx);
230
+ return fsReadFile(absolutePath);
231
+ },
232
+ access: async (absolutePath) => {
233
+ await assertReadablePath(absolutePath, ctx);
234
+ await fsAccess(absolutePath);
235
+ },
236
+ };
237
+ }
204
238
  /** Build FS operations that enforce writer policy before mutations. */
205
239
  export function createPiWriterFsOperations(ctx) {
240
+ const strictReadSet = (ctx.readSet?.length ?? 0) > 0;
206
241
  return {
207
- readFile: (absolutePath) => fsReadFile(absolutePath),
242
+ readFile: async (absolutePath) => {
243
+ if (strictReadSet)
244
+ await assertReadablePath(absolutePath, ctx);
245
+ return fsReadFile(absolutePath);
246
+ },
208
247
  access: async (absolutePath) => {
248
+ if (strictReadSet)
249
+ await assertReadablePath(absolutePath, ctx);
209
250
  await fsAccess(absolutePath);
210
251
  },
211
252
  writeFile: async (absolutePath, content) => {
@@ -218,6 +259,20 @@ export function createPiWriterFsOperations(ctx) {
218
259
  },
219
260
  };
220
261
  }
262
+ export async function createPiReaderCustomTools(ctx) {
263
+ const sdk = (await import("@earendil-works/pi-coding-agent"));
264
+ const createReadToolDefinition = sdk.createReadToolDefinition;
265
+ if (typeof createReadToolDefinition !== "function") {
266
+ throw new Error("pi read tool policy unavailable: createReadToolDefinition missing from SDK");
267
+ }
268
+ const read = createReadToolDefinition(path.resolve(ctx.repoRoot), {
269
+ operations: createPiReaderFsOperations(ctx),
270
+ });
271
+ if (read?.name !== "read") {
272
+ throw new Error("pi read tool policy unavailable: SDK tool must be named read");
273
+ }
274
+ return [read];
275
+ }
221
276
  /**
222
277
  * Create same-name custom edit/write tool definitions that override built-ins
223
278
  * via Pi SDK customTools registration. Fails closed when SDK factories are missing.
@@ -262,5 +317,6 @@ export function buildPiWriterToolPolicyContext(input) {
262
317
  writeSet: input.writeSet ?? [],
263
318
  forbiddenPaths: input.forbiddenPaths ?? [],
264
319
  writePolicy: input.writePolicy ?? "exclusive",
320
+ readSet: input.readSet ?? [],
265
321
  };
266
322
  }
@@ -540,13 +540,22 @@ async function executeBackendTestPipeline(input, meta) {
540
540
  const started = Date.now();
541
541
  // Plan A: prefer the frozen layout carried by the compiled spec; fall back
542
542
  // to the historical default for hand-written legacy DAGs.
543
- const backendLayout = resolveBackendTestLayout(meta.spec.backendTestLayout
543
+ const baseBackendLayout = resolveBackendTestLayout(meta.spec.backendTestLayout
544
544
  ? {
545
545
  testRoot: meta.spec.backendTestLayout.testRoot,
546
546
  markdownRel: meta.spec.backendTestLayout.markdownRel,
547
547
  scriptRel: meta.spec.backendTestLayout.scriptRel,
548
548
  }
549
549
  : undefined);
550
+ const markdownPlanArtifact = path.join(meta.runDir, "generate-backend-md-plan-pi", "plan.md");
551
+ const backendLayout = existsSync(markdownPlanArtifact)
552
+ ? {
553
+ ...baseBackendLayout,
554
+ readmePath: path
555
+ .relative(input.cwd, markdownPlanArtifact)
556
+ .replaceAll(path.sep, "/"),
557
+ }
558
+ : baseBackendLayout;
550
559
  try {
551
560
  if (!pipeline)
552
561
  throw new Error("missing backend-test pipeline id");
@@ -916,6 +925,7 @@ async function executeBackendTestPipeline(input, meta) {
916
925
  effective = await materializeEffectiveBackendPytestCollection({
917
926
  workspaceRoot: input.cwd,
918
927
  initial,
928
+ layout: backendLayout,
919
929
  });
920
930
  }
921
931
  else if (initial.status === "REPAIRABLE") {
@@ -975,6 +985,7 @@ async function executeBackendTestPipeline(input, meta) {
975
985
  workspaceRoot: input.cwd,
976
986
  initial,
977
987
  final: finalFacts,
988
+ layout: backendLayout,
978
989
  });
979
990
  }
980
991
  else {
@@ -1037,6 +1048,7 @@ async function executeBackendTestPipeline(input, meta) {
1037
1048
  scenarioParamRepairAttempt: scenarioFacts.repairAttempt,
1038
1049
  eligibility,
1039
1050
  eligibilityInputHashes,
1051
+ layout: backendLayout,
1040
1052
  });
1041
1053
  return {
1042
1054
  ok: true,
@@ -1145,7 +1157,7 @@ async function executeBackendTestPipeline(input, meta) {
1145
1157
  }
1146
1158
  else if (pipeline === "markdown-execute-html") {
1147
1159
  const executionReadiness = await readBackendTestExecutionReadiness(path.join(meta.runDir, "contracts", "backend-test-execution-readiness.json"));
1148
- await assertBackendTestExecutionReadinessFresh(input.cwd, executionReadiness);
1160
+ await assertBackendTestExecutionReadinessFresh(input.cwd, executionReadiness, backendLayout);
1149
1161
  const mappedScripts = await collectBackendTestMappedPytestScripts(input.cwd, backendLayout);
1150
1162
  let eligibleItemIds = executionReadiness.eligibleItemIds;
1151
1163
  // Plan D gap-fill executeScope=affected: when a gap plan exists in
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-BUY4oIzT.js";import{p as f}from"./chunk-JWPE2WC7-COV-RQk4.js";import{_ as n,l as o}from"./mermaid.core-D_WXK6C7.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-BxSD-LVV.js";import"./index-B0jGzQj6.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-DvbE1yQW.js";import{p as f}from"./chunk-JWPE2WC7-CcaZrq0k.js";import{_ as n,l as o}from"./mermaid.core-BYdXtke8.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CGRpxWNJ.js";import"./index-B4l5ag79.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
@@ -1 +1 @@
1
- import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-D_WXK6C7.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-BYdXtke8.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};