@tea-agent/loop-agent 0.44.0-next.12 → 0.44.0-next.14

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 (188) hide show
  1. package/CHANGELOG.md +27 -2
  2. package/README.md +12 -12
  3. package/dist/adapters/loop-agent.js +34 -0
  4. package/dist/build-stamp.json +3 -3
  5. package/dist/cli/command-definitions.js +5 -5
  6. package/dist/cli/program.js +1 -4
  7. package/dist/commands/init-legacy-audit.js +330 -0
  8. package/dist/commands/init-upgrade.js +644 -104
  9. package/dist/commands/init.js +686 -330
  10. package/dist/commands/operator-guide.js +99 -0
  11. package/dist/commands/operator.js +16 -3
  12. package/dist/executors/dag-pi-executor.js +11 -6
  13. package/dist/executors/pi-sdk-executor.js +3 -1
  14. package/dist/executors/pi-writer-tool-policy.js +60 -4
  15. package/dist/executors/playwright-cli-launcher.js +31 -45
  16. package/dist/executors/shell-write-guard.js +24 -67
  17. package/dist/governance/exec-plans.js +10 -1
  18. package/dist/governance/harness.js +2 -7
  19. package/dist/governance/manifest-types.js +28 -9
  20. package/dist/governance/project-capability-scanner.js +263 -0
  21. package/dist/governance/project-config.js +96 -0
  22. package/dist/governance/resource-catalog.js +151 -0
  23. package/dist/infrastructure/console/app-data.js +6 -2
  24. package/dist/shared/operator/capabilities.js +2 -2
  25. package/dist/shared/package-metadata.js +23 -7
  26. package/dist/shared/pi-context-pressure/extension.js +23 -0
  27. package/dist/shared/pi-context-pressure/index.js +1 -0
  28. package/dist/shared/pi-context-pressure/telemetry.js +4 -0
  29. package/dist/shared/pi-context-pressure/tool-result-pruner.js +98 -0
  30. package/dist/shared/update/npm-client.js +49 -1
  31. package/dist/task/config-types.js +4 -2
  32. package/dist/task/source-prepare/build-draft.js +1 -0
  33. package/dist/task/source-prepare/path-policy.js +17 -7
  34. package/dist/worker/cli.js +3 -5
  35. package/dist/worker/console/chat/browser-automation.js +455 -41
  36. package/dist/worker/console/chat/browser-content.js +74 -0
  37. package/dist/worker/console/chat/browser-diagnostics.js +250 -0
  38. package/dist/worker/console/chat/browser-evaluation.js +58 -0
  39. package/dist/worker/console/chat/browser-operation-queue.js +19 -0
  40. package/dist/worker/console/chat/browser-pick.js +113 -0
  41. package/dist/worker/console/chat/browser-routes.js +54 -5
  42. package/dist/worker/console/chat/browser-screenshot-result.js +47 -0
  43. package/dist/worker/console/chat/browser-storage.js +89 -0
  44. package/dist/worker/console/chat/chat-event-store.js +7 -6
  45. package/dist/worker/console/chat/context-insights.js +84 -19
  46. package/dist/worker/console/chat/pet/pet-affinity.js +169 -0
  47. package/dist/worker/console/chat/pet/pet-persist.js +109 -0
  48. package/dist/worker/console/chat/pet/pet-registry.js +142 -0
  49. package/dist/worker/console/chat/pet/pet-remarks.js +48 -0
  50. package/dist/worker/console/chat/pet/pet-routes.js +125 -0
  51. package/dist/worker/console/chat/pet/pet-service.js +262 -0
  52. package/dist/worker/console/chat/pet/pet-state.js +99 -0
  53. package/dist/worker/console/chat/pi-mode-loop-isolation.js +17 -2
  54. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +125 -174
  55. package/dist/worker/console/chat/pi-runtime.js +8 -3
  56. package/dist/worker/console/chat/repo-browser.js +80 -2
  57. package/dist/worker/console/chat/repo-preview-kind.js +155 -0
  58. package/dist/worker/console/chat/routes.js +43 -7
  59. package/dist/worker/console/chat/scheduled-goal-request.js +14 -6
  60. package/dist/worker/console/chat/session-mode.js +1 -1
  61. package/dist/worker/console/chat/session-store.js +24 -5
  62. package/dist/worker/console/chat/tools.js +12 -0
  63. package/dist/worker/console/console-shutdown.js +34 -0
  64. package/dist/worker/console/server.js +2 -1
  65. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-r1NAPU_5.js → abnfDiagram-N423BO3Z-DASc69AM.js} +1 -1
  66. package/dist/worker/console/static/assets/{arc-dT5T4sVL.js → arc-h09p-LAc.js} +1 -1
  67. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CP_cOq46.js → architectureDiagram-T3A2C74G-CZ3dtLkh.js} +1 -1
  68. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-sToEZxEf.js → blockDiagram-VBNYF7ZC-DMrRhMXG.js} +1 -1
  69. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DSxwwi2m.js → c4Diagram-5PPSVZJV-DQxisHV6.js} +1 -1
  70. package/dist/worker/console/static/assets/channel-D1zgKZfg.js +1 -0
  71. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-B9fh0IkF.js → chunk-2GRJ4B5K-CefVN9zh.js} +1 -1
  72. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CAUNl-A6.js → chunk-2Q5K7J3B-DmR1Koq2.js} +1 -1
  73. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BomeqbNX.js → chunk-5RXB4S5H-NlzrSW7L.js} +1 -1
  74. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BNAuTOLE.js → chunk-5VM5RSS4-DfelJv-0.js} +1 -1
  75. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DfzjRD5y.js → chunk-6Q2QTUOP-BK6RFy58.js} +1 -1
  76. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C3uE-yc2.js → chunk-GF5L2VYU-BAeh9lz4.js} +1 -1
  77. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-C3sQMMYr.js → chunk-JWPE2WC7-ChPS5WzG.js} +1 -1
  78. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-D_AtEYQL.js → chunk-KBJHAD2P-BoVe5IM6.js} +1 -1
  79. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DW4TwhKv.js → chunk-RYQCIY6F-C4q_0Lw9.js} +1 -1
  80. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CTQDmJcp.js → chunk-XXDRQBXY-Bb9TvjeY.js} +1 -1
  81. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CXNNsY2M.js +1 -0
  82. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CXNNsY2M.js +1 -0
  83. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-iMqPuBsL.js → cose-bilkent-JH36ORCC-DmNnL8Ff.js} +1 -1
  84. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DktIuDWs.js → cynefin-VYW2F7L2-Contvx58.js} +1 -1
  85. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BgVnjyEs.js → cynefinDiagram-MW4NZA55-Cigq7cjk.js} +1 -1
  86. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CR25E4bu.js → dagre-VZM6K2ZE-dEbC0nxb.js} +1 -1
  87. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CPM2h5HY.js → diagram-7IWD3JNH-CzIztESV.js} +1 -1
  88. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DqF0x2g1.js → diagram-B4RE2ZJO-Bo14zbaY.js} +1 -1
  89. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-G8BYp2eb.js → diagram-LBJQPF4R-C4xUyil6.js} +1 -1
  90. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BbC4kT_D.js → diagram-Q27KOJAE-CaBY_uYa.js} +1 -1
  91. package/dist/worker/console/static/assets/{diagram-UB23O5K3-CKZwZ-G5.js → diagram-UB23O5K3-ClUXUeiu.js} +1 -1
  92. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-kJDmheia.js → ebnfDiagram-BXEA7PRR-BbGCgjC6.js} +1 -1
  93. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DFTVHyxB.js → erDiagram-JOGREHBK-Dr-AcwSl.js} +1 -1
  94. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BqdN5_os.js → flowDiagram-UKHOOZJN-BG8Gcfyc.js} +1 -1
  95. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-SYDjVakh.js → ganttDiagram-PKOTCBZU-CFVaPs5r.js} +1 -1
  96. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bf8bNpGA.js → gitGraphDiagram-DS77QQ5N-ChJ8pm-O.js} +1 -1
  97. package/dist/worker/console/static/assets/index-B_5GhVIO.js +488 -0
  98. package/dist/worker/console/static/assets/index-C7YMBluX.css +1 -0
  99. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DIeAmJ45.js → infoDiagram-6WML65LV-C5J9-uCZ.js} +1 -1
  100. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-oikqUAlo.js → ishikawaDiagram-WSZJBQD7-BcfjqzDv.js} +1 -1
  101. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-mKEF_xYT.js → journeyDiagram-NVQOT4AX-BzzCO-rD.js} +1 -1
  102. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BdWJHPnN.js → kanban-definition-27J2QSJJ-NCHUZ46h.js} +1 -1
  103. package/dist/worker/console/static/assets/{linear-DGRlieMH.js → linear-BKZlWCUU.js} +1 -1
  104. package/dist/worker/console/static/assets/{mermaid.core-4P9yPC9c.js → mermaid.core-DbTaQjFE.js} +5 -5
  105. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-5PTSkos6.js → mindmap-definition-FAOFIHXS-CroYl5xQ.js} +1 -1
  106. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DONIFpG5.js → pegDiagram-VL7TDLO6-DgMBx-9k.js} +1 -1
  107. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-D_Z4S70M.js → pieDiagram-7S7Q4E2Y-CyWDmbq1.js} +1 -1
  108. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CAsjh8iY.js → quadrantDiagram-CIZ2JOQS-BgGEOqSP.js} +1 -1
  109. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Cx4Dd6ac.js → railroadDiagram-AXF67PYL-BMZ7oeeV.js} +1 -1
  110. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-cQb7mwyH.js → requirementDiagram-LRYGKXZP-DxqyGhcc.js} +1 -1
  111. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cd-DhUdf.js → sankeyDiagram-W5VNT64P-DnSvNCG8.js} +1 -1
  112. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-AlKYRgIk.js → sequenceDiagram-SI44F4Z6-BiZh6f-R.js} +1 -1
  113. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CUy_ET4e.js → sizeCapture-X5ZJPWSS-BKXSI8WB.js} +1 -1
  114. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-CTuVe6Sa.js → stateDiagram-OKZ733FA-CfSgoYFm.js} +1 -1
  115. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DgHi3vjb.js +1 -0
  116. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-ChFLJb-v.js → swimlanes-SLNWSIFB-CyGADcHe.js} +2 -2
  117. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bgvdt1Y0.js +8 -0
  118. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DZZeiAPr.js → timeline-definition-Z64GVDOM-HmPaSPOK.js} +1 -1
  119. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-t4O91cZU.js → vennDiagram-T6HMQDX7-VA2VBbez.js} +1 -1
  120. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CV5qGHZu.js → wardleyDiagram-T6FBY63Y-BxjiBPZa.js} +1 -1
  121. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DyXbytUI.js → xychartDiagram-ELKLHX3M--hUY-qFj.js} +1 -1
  122. package/dist/worker/console/static/index.html +2 -2
  123. package/dist/worker/console/static/pets/ember/pet.json +167 -0
  124. package/dist/worker/console/static/pets/ember/spritesheet.png +0 -0
  125. package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +4 -3
  126. package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +57 -9
  127. package/dist/worker/console/static-src/operator-chat/use-stat-dialog.js +95 -0
  128. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +6 -0
  129. package/dist/worker/console/workspace-context.js +15 -0
  130. package/dist/worker/console/workspace-initialization.js +25 -16
  131. package/dist/worker/delivery/package.js +4 -1
  132. package/dist/worker/materialize/harness-task-lineage.js +4 -2
  133. package/dist/worker/observe/static/console-theme.css +33 -29
  134. package/dist/worker/observe/static/operator-chrome.js +1 -42
  135. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +3 -1
  136. package/dist/workflows/dag/backend-test-layout.js +27 -7
  137. package/dist/workflows/dag/backend-test-markdown-workflow.js +3 -1
  138. package/dist/workflows/dag/backend-test-pytest-collection.js +4 -1
  139. package/dist/workflows/dag/final-verification.js +45 -10
  140. package/dist/workflows/dag/frontend-test-case-checklist.js +12 -3
  141. package/dist/workflows/dag/frontend-test-environment-probe.js +0 -2
  142. package/dist/workflows/dag/frontend-test-layout.js +41 -12
  143. package/dist/workflows/dag/hybrid/sources.js +35 -16
  144. package/dist/workflows/dag/hybrid/templates/backend-test.js +70 -55
  145. package/dist/workflows/dag/hybrid/templates/frontend-test.js +62 -41
  146. package/dist/workflows/dag/hybrid/templates/kg-bootstrap.js +6 -6
  147. package/dist/workflows/dag/hybrid/templates/knowledge-sync.js +5 -5
  148. package/dist/workflows/dag/skill-instructions.js +102 -32
  149. package/dist/workflows/dag/skill-snapshot.js +3 -0
  150. package/dist/workflows/dag/skills.js +7 -7
  151. package/dist/workflows/dag/test-artifact-namespace.js +73 -0
  152. package/dist/workflows/dag/validate.js +24 -5
  153. package/dist/workflows/dag/workspace-checkpoint.js +13 -1
  154. package/docs/architecture/runtime-boundaries.md +28 -22
  155. package/docs/init-surface.manifest.json +43 -295
  156. package/docs/templates/README.md +4 -4
  157. package/docs/templates/agent-dag.base.json +9 -9
  158. package/docs/templates/agent-dag.final-verification.json +6 -6
  159. package/docs/templates/agent-dag.supervised-implementation.json +12 -12
  160. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +3 -3
  161. package/docs/templates/backend-test-dag.json +708 -708
  162. package/docs/templates/backend-test-dag.review-cases.prompt.md +3 -3
  163. package/docs/templates/frontend-implementation-dag.json +2 -2
  164. package/docs/templates/frontend-test-case-checklist.md +2 -2
  165. package/docs/templates/frontend-test-dag.generate-cases.prompt.md +7 -7
  166. package/docs/templates/frontend-test-dag.json +48 -39
  167. package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +2 -2
  168. package/docs/templates/frontend-test-dag.retrospect.prompt.md +1 -1
  169. package/docs/templates/frontend-test-dag.review-cases.prompt.md +1 -1
  170. package/docs/templates/hybrid-dag.json +9 -9
  171. package/docs/templates/knowledge-sync-dag.json +8 -8
  172. package/package.json +12 -3
  173. package/resource-catalog.v1.json +1973 -0
  174. package/resources/agent-bridge/loop-agent/SKILL.md +40 -0
  175. package/resources/init-history/legacy-surface.v1.json +33 -0
  176. package/skills/fe-test-ui-scout/SKILL.md +4 -4
  177. package/skills/fe-test-ui-scout/references/ledger-schema.md +1 -1
  178. package/skills/init-update/SKILL.md +34 -0
  179. package/skills/init-update/agents/openai.yaml +6 -0
  180. package/skills/playwright-cli/SKILL.md +1 -1
  181. package/skills/playwright-cli-case-generator/SKILL.md +4 -4
  182. package/dist/worker/console/static/assets/channel-CyBY_yGk.js +0 -1
  183. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D5lN4E_E.js +0 -1
  184. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D5lN4E_E.js +0 -1
  185. package/dist/worker/console/static/assets/index-B28Onmfy.css +0 -1
  186. package/dist/worker/console/static/assets/index-CTMZsDmh.js +0 -486
  187. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BsWhs8O7.js +0 -1
  188. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CJcioYGH.js +0 -8
@@ -142,12 +142,18 @@ function executableListStep(value) {
142
142
  return null;
143
143
  return normalizeCommand(inlineCode[1]);
144
144
  }
145
- const prompted = /^[$>]\s*\S/.test(trimmed);
146
145
  const normalized = normalizeCommand(trimmed);
147
146
  if (isNonExecutableBlockedProse(normalized))
148
147
  return null;
149
- if (prompted || isCommandLikeExecutable(normalized))
148
+ // A `$ `/`> ` prompt marker (stripped by normalizeCommand) must NOT turn
149
+ // Markdown documentation — e.g. a `> 协议说明` blockquote inside 测试步骤 —
150
+ // into an executable instruction: every line, prompted or not, follows the
151
+ // same command-like rule. Real commands after the marker (e.g.
152
+ // `> npx playwright test`) still normalize to command-like executables and
153
+ // stay gated by the allowlist.
154
+ if (isCommandLikeExecutable(normalized)) {
150
155
  return normalized;
156
+ }
151
157
  return null;
152
158
  }
153
159
  function hasShellControl(command) {
@@ -330,7 +336,10 @@ export async function validateFrontendCaseChecklist(input) {
330
336
  }
331
337
  if (expectedPath && casePath.replaceAll("\\", "/") !== expectedPath)
332
338
  issues.push({ ruleId: "case-path-mismatch", caseId: id, casePath, detail: `${casePath} must equal ${expectedPath}` });
333
- const body = await readFile(absolute, "utf8");
339
+ // `playwright_cli` (underscore) is the structured tool name; normalize it to
340
+ // the canonical `playwright-cli` executable spelling before gating so the
341
+ // same command is accepted regardless of which spelling the case used.
342
+ const body = (await readFile(absolute, "utf8")).replace(/\bplaywright_cli\b/g, "playwright-cli");
334
343
  if (!openRe.test(body))
335
344
  issues.push({ ruleId: "open-prefix", caseId: id, casePath, detail: "missing playwright-cli open --browser=chrome <absolute-url>" });
336
345
  const match = body.match(/playwright-cli\s+open\s+--browser=chrome\s+(https?:\/\/\S+)/i);
@@ -2,8 +2,6 @@ import { spawnSync } from "node:child_process";
2
2
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { defaultFrontendTestLayout, frontendTestContextRel, frontendTestProbeRel, } from "./frontend-test-layout.js";
5
- export const FRONTEND_TEST_CONTEXT_REL = "testcase/frontend/rag/context.md";
6
- export const FRONTEND_TEST_PROBE_REL = "testcase/frontend/rag/environment-probe.json";
7
5
  const CONNECTION_REFUSED_HINT = "被测服务未在 baseUrl 监听(connection refused)。请先启动本地前端(例如 scripts/serve.sh 或项目约定的 npm start),确认可访问后再从 materialize-frontend-test-execution-shell 续跑。";
8
6
  export function classifyFrontendBaseUrlProbeFailure(input) {
9
7
  if (input.spawnError)
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { canonicalTestArtifactRoot, } from "./test-artifact-namespace.js";
2
3
  /**
3
4
  * Frontend-test artifact layout (configurable testRoot).
4
5
  *
@@ -6,14 +7,16 @@ import path from "node:path";
6
7
  * frozen on the spec for the whole run. Writers, gates, Playwright fences
7
8
  * and TaskSpec allowedPaths all derive from this single object.
8
9
  *
9
- * Default layout keeps the historical frozen contract:
10
- * testcase/frontend/rag/**
11
- * testcase/frontend/cases/**
12
- * testcase/frontend/evidence/**
13
- * testcase/frontend/reports/**
14
- * testcase/frontend/fixtures/**
10
+ * Default layout (since 2026-09-17) lives under the shared runtime namespace:
11
+ * .harness/testcase/frontend/rag/**
12
+ * .harness/testcase/frontend/cases/**
13
+ * .harness/testcase/frontend/evidence/**
14
+ * .harness/testcase/frontend/reports/**
15
+ * .harness/testcase/frontend/fixtures/**
15
16
  */
16
- export const FRONTEND_TEST_DEFAULT_TEST_ROOT = "testcase/frontend";
17
+ export const FRONTEND_TEST_DEFAULT_TEST_ROOT = canonicalTestArtifactRoot("frontend-test");
18
+ /** Historical pre-2026-09-17 default root, kept for text rewrites only. */
19
+ export const FRONTEND_TEST_LEGACY_DEFAULT_TEST_ROOT = "testcase/frontend";
17
20
  const FORBIDDEN_TEST_ROOT_PREFIXES = [
18
21
  "src",
19
22
  "app",
@@ -59,6 +62,10 @@ function normalizeRelPosix(value, label) {
59
62
  return segments.join("/");
60
63
  }
61
64
  function assertTestRootAllowed(testRoot) {
65
+ // D3: the canonical harness test-artifact root is the ONLY hidden-root
66
+ // exception. Every other hidden or forbidden top segment stays rejected.
67
+ if (testRoot === FRONTEND_TEST_DEFAULT_TEST_ROOT)
68
+ return;
62
69
  const topSegment = testRoot.split("/")[0];
63
70
  if (FORBIDDEN_TEST_ROOT_PREFIXES.some((forbidden) => topSegment === forbidden || topSegment.startsWith(`${forbidden}.`))) {
64
71
  throw new Error(`frontendTest.testRoot must not target production/governance trees: ${testRoot}`);
@@ -67,14 +74,25 @@ function assertTestRootAllowed(testRoot) {
67
74
  throw new Error(`frontendTest.testRoot must not target hidden directories: ${testRoot}`);
68
75
  }
69
76
  }
77
+ function resolveTestRootInput(raw, canonical) {
78
+ // D3: only the EXACT canonical harness test-artifact root may be a hidden
79
+ // path. Compare after trimming + stripping trailing slashes so
80
+ // `.harness/testcase/frontend/` matches, but every other hidden spelling
81
+ // falls through to the fail-closed normalizer below.
82
+ const trimmed = raw.trim().replace(/\/+$/, "");
83
+ if (trimmed === canonical)
84
+ return canonical;
85
+ return normalizeRelPosix(raw, "testRoot");
86
+ }
70
87
  /**
71
88
  * Resolve the frozen frontend-test artifact layout from task config.
72
- * Throws on unsafe or ambiguous configuration (fail-closed).
89
+ * Roots of the form `.harness/testcase/frontend` are accepted only as the
90
+ * exact canonical default; all other hidden roots fail closed (D3).
73
91
  */
74
92
  export function resolveFrontendTestLayout(config) {
75
93
  const testRoot = config?.testRoot === undefined || config?.testRoot === null
76
94
  ? FRONTEND_TEST_DEFAULT_TEST_ROOT
77
- : normalizeRelPosix(config.testRoot, "testRoot");
95
+ : resolveTestRootInput(config.testRoot, FRONTEND_TEST_DEFAULT_TEST_ROOT);
78
96
  assertTestRootAllowed(testRoot);
79
97
  const isDefault = testRoot === FRONTEND_TEST_DEFAULT_TEST_ROOT;
80
98
  return {
@@ -98,10 +116,16 @@ export function frontendTestLayoutFromSpec(spec) {
98
116
  return resolveFrontendTestLayout({ testRoot: frozen.testRoot });
99
117
  }
100
118
  const DEFAULT_PREFIX = `${FRONTEND_TEST_DEFAULT_TEST_ROOT}/`;
119
+ const LEGACY_DEFAULT_PREFIX = `${FRONTEND_TEST_LEGACY_DEFAULT_TEST_ROOT}/`;
101
120
  /**
102
- * Longest-prefix rewrite of historical `testcase/frontend/` paths.
103
- * No-op when layout.isDefault so default DAGs stay byte-identical.
104
- * Never replaces the short token `testcase/` (would collide with backend-test).
121
+ * Longest-prefix rewrite of frontend test-root paths in template text.
122
+ *
123
+ * No-op when layout.isDefault (default DAGs stay byte-identical to their
124
+ * templates, which already use the canonical `.harness/testcase/frontend`
125
+ * root). Custom roots rewrite BOTH the canonical default prefix and the
126
+ * historical `testcase/frontend/` prefix, so legacy templates and frozen
127
+ * specs converge onto the configured root. Never replaces the short token
128
+ * `testcase/` alone (would collide with backend-test).
105
129
  */
106
130
  export function applyFrontendTestLayoutToText(text, layout) {
107
131
  if (layout.isDefault)
@@ -119,6 +143,11 @@ export function applyFrontendTestLayoutToText(text, layout) {
119
143
  i += DEFAULT_PREFIX.length;
120
144
  continue;
121
145
  }
146
+ if (text.startsWith(LEGACY_DEFAULT_PREFIX, i)) {
147
+ output += replacement;
148
+ i += LEGACY_DEFAULT_PREFIX.length;
149
+ continue;
150
+ }
122
151
  output += text[i];
123
152
  i += 1;
124
153
  }
@@ -7,6 +7,7 @@ import { DAG_AGENT_RUNTIME_PI_ONLY, DAG_REPAIR_WRITER_PROTOCOL_EXPLICIT_NODE_V1,
7
7
  import { planMavenVerification } from "../../../verification/maven/index.js";
8
8
  import { pathMatchesPattern } from "../../../shared/git-progress.js";
9
9
  import { BASELINE_FORBIDDEN_PATHS } from "../governance-constants.js";
10
+ import { isAnyTestArtifactPath } from "../test-artifact-namespace.js";
10
11
  import { resolveAdapter } from "../../../adapters/index.js";
11
12
  import { loadHarnessManifest } from "../../../governance/harness.js";
12
13
  import { mergeDocumentIndexCompanions } from "../../../governance/document-index-closure.js";
@@ -31,16 +32,16 @@ export const HYBRID_DEFAULTS = {
31
32
  executor: "pi",
32
33
  piBackend: "sdk-first",
33
34
  contextProfile: "slim",
34
- skills: ["ai-engineering-context"],
35
+ skills: ["builtin:ai-engineering-context"],
35
36
  writePolicy: "read-only",
36
37
  };
37
38
  export const HYBRID_SKILLS_BY_ROLE = {
38
- planner: ["loop-agent"],
39
- scout: ["ai-engineering-context", "codebase-scout"],
40
- implementer: ["test-driven-development", "verification-before-completion"],
41
- reviewer: ["requesting-code-review", "code-review-core"],
42
- verifier: ["verification-before-completion", "systematic-debugging"],
43
- closeout: ["loop-agent", "verification-before-completion"],
39
+ planner: ["builtin:loop-agent"],
40
+ scout: ["builtin:ai-engineering-context", "builtin:codebase-scout"],
41
+ implementer: ["builtin:test-driven-development", "builtin:verification-before-completion"],
42
+ reviewer: ["builtin:requesting-code-review", "builtin:code-review-core"],
43
+ verifier: ["builtin:verification-before-completion", "builtin:systematic-debugging"],
44
+ closeout: ["builtin:loop-agent", "builtin:verification-before-completion"],
44
45
  };
45
46
  export const FRONTEND_DEFAULTS = {
46
47
  ...HYBRID_DEFAULTS,
@@ -55,13 +56,13 @@ export const FRONTEND_SKILLS_BY_ROLE = {
55
56
  verifier: [],
56
57
  closeout: [],
57
58
  };
58
- export const FRONTEND_CONTRACT_SKILLS = ["frontend-contract"];
59
- export const FRONTEND_SCOUT_SKILLS = ["frontend-scout"];
60
- export const FRONTEND_PLAN_SKILLS = ["frontend-plan"];
61
- export const FRONTEND_BOUNDED_IMPLEMENT_SKILLS = ["frontend-bounded-implement"];
62
- export const FRONTEND_DESIGN_REVIEW_SKILLS = ["frontend-design-review"];
63
- export const FRONTEND_REVIEW_SKILLS = ["frontend-review"];
64
- export const FRONTEND_VERIFICATION_SKILLS = ["frontend-verification"];
59
+ export const FRONTEND_CONTRACT_SKILLS = ["builtin:frontend-contract"];
60
+ export const FRONTEND_SCOUT_SKILLS = ["builtin:frontend-scout"];
61
+ export const FRONTEND_PLAN_SKILLS = ["builtin:frontend-plan"];
62
+ export const FRONTEND_BOUNDED_IMPLEMENT_SKILLS = ["builtin:frontend-bounded-implement"];
63
+ export const FRONTEND_DESIGN_REVIEW_SKILLS = ["builtin:frontend-design-review"];
64
+ export const FRONTEND_REVIEW_SKILLS = ["builtin:frontend-review"];
65
+ export const FRONTEND_VERIFICATION_SKILLS = ["builtin:frontend-verification"];
65
66
  export const IMPLEMENT_WRITESET_PLACEHOLDER = "REPLACE/WITH/NARROW/IMPLEMENT/PATHS/**";
66
67
  export const GENERATED_DAG_RUNTIME_CONTRACT = {
67
68
  schemaVersion: DAG_RUNTIME_CONTRACT_SCHEMA_VERSION,
@@ -1534,20 +1535,38 @@ export function patternStaticPrefix(pattern) {
1534
1535
  const wildcard = normalized.search(/[?*]/);
1535
1536
  return (wildcard >= 0 ? normalized.slice(0, wildcard) : normalized).replace(/\/+$/, "");
1536
1537
  }
1537
- export function allowedPathHitsBaselineForbidden(allowedPath) {
1538
+ export function allowedPathHitsBaselineForbidden(allowedPath, options) {
1538
1539
  const probes = probePathsForWriteSetEntry(allowedPath);
1539
1540
  for (const forbidden of BASELINE_FORBIDDEN_PATHS) {
1540
1541
  if (probes.some((probe) => pathMatchesPattern(probe, forbidden)) ||
1541
1542
  pathMatchesPattern(allowedPath, forbidden) ||
1542
1543
  pathMatchesPattern(forbidden, allowedPath)) {
1544
+ // D3 test-artifact exception: dedicated test tasks may own exactly the
1545
+ // canonical `.harness/testcase` roots in task allowedPaths; every other
1546
+ // `.harness` overlap stays a baseline violation.
1547
+ if (options?.testArtifactNamespace &&
1548
+ isHarnessNamespacePattern(forbidden) &&
1549
+ probes.every((probe) => isAnyTestArtifactPath(probe))) {
1550
+ continue;
1551
+ }
1543
1552
  return forbidden;
1544
1553
  }
1545
1554
  }
1546
1555
  return undefined;
1547
1556
  }
1557
+ function isHarnessNamespacePattern(pattern) {
1558
+ const normalized = pattern.replace(/\\/g, "/").replace(/^\.\//, "");
1559
+ return (normalized === ".harness" ||
1560
+ normalized === ".harness/**" ||
1561
+ normalized.startsWith(".harness/"));
1562
+ }
1548
1563
  export function assertTaskAllowedPathsPreflight(taskConfig) {
1564
+ const testArtifactNamespace = taskConfig.taskKind === "backend-test" ||
1565
+ taskConfig.taskKind === "frontend-test";
1549
1566
  for (const allowedPath of taskConfig.allowedPaths) {
1550
- const forbidden = allowedPathHitsBaselineForbidden(allowedPath);
1567
+ const forbidden = allowedPathHitsBaselineForbidden(allowedPath, {
1568
+ testArtifactNamespace,
1569
+ });
1551
1570
  if (forbidden) {
1552
1571
  throw new Error(`task allowedPaths entry "${allowedPath}" overlaps baseline forbidden path "${forbidden}"; task source paths are main-session owned and must not be executor writeSet`);
1553
1572
  }