@tea-agent/loop-agent 0.42.0-next.6 → 0.42.0-next.8

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 (115) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +20 -0
  3. package/dist/build-stamp.json +3 -3
  4. package/dist/executors/dag-pi-executor.js +74 -13
  5. package/dist/executors/shell-executor.js +3 -1
  6. package/dist/task/source-prepare/fragment-inventory.js +49 -16
  7. package/dist/task/source-prepare/prepare.js +37 -20
  8. package/dist/worker/console/chat/model-resolver.js +7 -4
  9. package/dist/worker/console/chat/pi-runtime.js +60 -5
  10. package/dist/worker/console/chat/routes.js +69 -46
  11. package/dist/worker/console/chat/sdd-data-alignment.js +6 -3
  12. package/dist/worker/console/chat/session-store.js +16 -0
  13. package/dist/worker/console/pi-readiness.js +3 -2
  14. package/dist/worker/console/server.js +19 -1
  15. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BJpsGgAv.js → abnfDiagram-N423BO3Z-C6n9_m0P.js} +1 -1
  16. package/dist/worker/console/static/assets/{arc-DjMFPsEd.js → arc-DQh-IfZ1.js} +1 -1
  17. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DqdZlr1O.js → architectureDiagram-T3A2C74G-54NnrwUC.js} +1 -1
  18. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-BmZnfFEO.js → blockDiagram-VBNYF7ZC-pivRALGK.js} +1 -1
  19. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-3cKberpL.js → c4Diagram-5PPSVZJV-BR7OV2NJ.js} +1 -1
  20. package/dist/worker/console/static/assets/channel-DsZxrgqe.js +1 -0
  21. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-q-0yjQ1h.js → chunk-2GRJ4B5K-B1Aq6BcK.js} +1 -1
  22. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DnONBaDW.js → chunk-2Q5K7J3B-CbdU0rjo.js} +1 -1
  23. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-v3a6PCOS.js → chunk-5RXB4S5H-Dqi2GJeD.js} +1 -1
  24. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-D1kLrlU_.js → chunk-5VM5RSS4-BYn1Hu4R.js} +1 -1
  25. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BPelzihi.js → chunk-6Q2QTUOP-DRLjDL0k.js} +1 -1
  26. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-DauNE69H.js → chunk-GF5L2VYU-CY9Xx2jV.js} +1 -1
  27. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-RIg5_bFJ.js → chunk-JWPE2WC7-BNCZs7_z.js} +1 -1
  28. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BuILuoxs.js → chunk-KBJHAD2P-DZ8AStLL.js} +1 -1
  29. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-VHORV5oW.js → chunk-RYQCIY6F-DsxjYrzz.js} +1 -1
  30. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BJYr4xHC.js → chunk-XXDRQBXY-Ddx1KC1l.js} +1 -1
  31. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +1 -0
  32. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +1 -0
  33. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BS6nuqQn.js → cose-bilkent-JH36ORCC-W9TveCnK.js} +1 -1
  34. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BBhF0NAT.js → cynefin-VYW2F7L2-l9j_ztZH.js} +1 -1
  35. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D1135Ifg.js → cynefinDiagram-MW4NZA55-BMJMKi4G.js} +1 -1
  36. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-vAOYPUWJ.js → dagre-VZM6K2ZE-Bb4mG9pH.js} +1 -1
  37. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DuaSPkfa.js → diagram-7IWD3JNH-BMgL_Qv0.js} +1 -1
  38. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-mMvXvGU6.js → diagram-B4RE2ZJO-Bvo2T4OQ.js} +1 -1
  39. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CDIEJtlL.js → diagram-LBJQPF4R-_5kWRN9c.js} +1 -1
  40. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Bi_6JvCW.js → diagram-Q27KOJAE-DqdMrltM.js} +1 -1
  41. package/dist/worker/console/static/assets/{diagram-UB23O5K3-DAqj57yN.js → diagram-UB23O5K3-CDDYsNkp.js} +1 -1
  42. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DfWaxNEo.js → ebnfDiagram-BXEA7PRR-c4nfnZUV.js} +1 -1
  43. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bt7QqkNG.js → erDiagram-JOGREHBK-DnkUcNMs.js} +1 -1
  44. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CRRhQwT2.js → flowDiagram-UKHOOZJN-Dz0KGLZE.js} +1 -1
  45. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DAV1O5B6.js → ganttDiagram-PKOTCBZU-Cj1t1uka.js} +1 -1
  46. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-0X2Qo4Lp.js → gitGraphDiagram-DS77QQ5N-tp2FrBHd.js} +1 -1
  47. package/dist/worker/console/static/assets/index-D9Sc0f0y.js +449 -0
  48. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C5W5eILC.js → infoDiagram-6WML65LV-DQwJS8DH.js} +1 -1
  49. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-5SfqU-Tb.js → ishikawaDiagram-WSZJBQD7-eimCQWD4.js} +1 -1
  50. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CKDRcaKf.js → journeyDiagram-NVQOT4AX-Dd9Gbwgv.js} +1 -1
  51. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Cc7_TuSb.js → kanban-definition-27J2QSJJ-Qve3cyft.js} +1 -1
  52. package/dist/worker/console/static/assets/{linear-BcQYYmeT.js → linear-UXKSe36Z.js} +1 -1
  53. package/dist/worker/console/static/assets/{mermaid.core-CwLZPtQo.js → mermaid.core-CWhj4JXN.js} +5 -5
  54. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D29M025z.js → mindmap-definition-FAOFIHXS-yTcwf4SJ.js} +1 -1
  55. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CV-ZAWYe.js → pegDiagram-VL7TDLO6-_7qToaZP.js} +1 -1
  56. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-C6iPS_o5.js → pieDiagram-7S7Q4E2Y-CXmrKmDm.js} +1 -1
  57. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-BFCD1sTg.js → quadrantDiagram-CIZ2JOQS-hMmrJyaS.js} +1 -1
  58. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CztNj4O9.js → railroadDiagram-AXF67PYL-BXw8tCBe.js} +1 -1
  59. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BWIELSNd.js → requirementDiagram-LRYGKXZP-BmjgnLZl.js} +1 -1
  60. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CPtxrjJQ.js → sankeyDiagram-W5VNT64P-Bj77SPpN.js} +1 -1
  61. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BEWVSFsu.js → sequenceDiagram-SI44F4Z6-B2FDVysL.js} +1 -1
  62. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DyhzoX_J.js → sizeCapture-X5ZJPWSS-UChNY9ZZ.js} +1 -1
  63. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-xLox0nu7.js → stateDiagram-OKZ733FA-wbAEkbd7.js} +1 -1
  64. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +1 -0
  65. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CLC_ow9A.js → swimlanes-SLNWSIFB-DCEkU8HR.js} +2 -2
  66. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +8 -0
  67. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DmJZ4uyY.js → timeline-definition-Z64GVDOM-BGp7H06U.js} +1 -1
  68. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DD7GGi9Z.js → vennDiagram-T6HMQDX7-DsCIQuWR.js} +1 -1
  69. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Bi8PQoGt.js → wardleyDiagram-T6FBY63Y-cRZLP4Xe.js} +1 -1
  70. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-uKdAY8Dp.js → xychartDiagram-ELKLHX3M-CP0xLR9Y.js} +1 -1
  71. package/dist/worker/console/static/index.html +1 -1
  72. package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
  73. package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
  74. package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
  75. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +31 -2
  76. package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -3
  77. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
  78. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
  79. package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
  80. package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
  81. package/dist/worker/observe/static/dag-helpers.js +4 -2
  82. package/dist/worker/observe/static/inspect-workspace.js +23 -0
  83. package/dist/worker/observe/static/kpi.js +2 -2
  84. package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
  85. package/dist/worker/observe/static/operator-chrome.js +37 -23
  86. package/dist/worker/observe/static/relations.js +2 -2
  87. package/dist/worker/observe/static/router.d.ts +12 -1
  88. package/dist/worker/observe/static/router.js +48 -6
  89. package/dist/worker/observe/static/run-processing.js +4 -2
  90. package/dist/worker/observe/static/shell-chrome.js +2 -2
  91. package/dist/worker/observe/static/state.js +5 -0
  92. package/dist/worker/observe/static/styles.css +95 -0
  93. package/dist/worker/observe/static/views/dag-inspector.js +14 -7
  94. package/dist/worker/observe/static/views/dag.js +10 -2
  95. package/dist/worker/observe/static/views/dags.js +3 -1
  96. package/dist/worker/observe/static/views/dashboard.js +14 -6
  97. package/dist/worker/observe/static/views/pool.js +7 -2
  98. package/dist/worker/observe/static/views/run.js +12 -3
  99. package/dist/worker/observe/static/views/session-timeline.js +69 -10
  100. package/dist/worker/observe/static/views/task.js +7 -2
  101. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +40 -9
  102. package/dist/workflows/dag/backend-test-scenario-param.js +126 -19
  103. package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
  104. package/dist/workflows/dag/backend-test-writer-completeness.js +7 -3
  105. package/dist/workflows/dag/frontend-shadow-dual-write.js +33 -6
  106. package/dist/workflows/dag/init-hybrid.js +175 -27
  107. package/dist/workflows/dag/node-execution.js +1 -1
  108. package/dist/workflows/dag/types.js +1 -1
  109. package/package.json +3 -2
  110. package/dist/worker/console/static/assets/channel-DWYwciiv.js +0 -1
  111. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D6jOf_l4.js +0 -1
  112. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D6jOf_l4.js +0 -1
  113. package/dist/worker/console/static/assets/index-CVdddQsA.js +0 -451
  114. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DS5ZRvJG.js +0 -1
  115. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Dvpxn-bf.js +0 -8
@@ -26,7 +26,7 @@ import { CANONICAL_TASK_ID_PATTERN, getTaskPaths, loadTaskConfig } from "../../t
26
26
  import { materializeTaskReferenceDocs } from "../../task/source-references.js";
27
27
  import { observeTaskContract } from "../../task/contract/observe.js";
28
28
  import { extractRequirementFactsFromMarkdown } from "../../task/source-prepare/parse-intent.js";
29
- import { computeLedgerInputDigest, parseLedgerJson, recoverLedgerInputContract, } from "../../task/source-prepare/ledger.js";
29
+ import { computeLedgerInputDigest, parseLedgerJson, recoverLedgerInputContract, validateRequirementLedger, } from "../../task/source-prepare/ledger.js";
30
30
  import { REQUIREMENT_LEDGER_FILE_NAME } from "../../task/contract/constants.js";
31
31
  import { dagHasWriterExecution } from "./task-contract-binding.js";
32
32
  import { DEFAULT_VERIFY_TIMEOUT_MS, resolveVerifyPreset, } from "../../executors/shell-verification.js";
@@ -1224,21 +1224,121 @@ function canonicalizeVerificationCommands(commands) {
1224
1224
  }
1225
1225
  function assertVerificationPlanPreflight(input) {
1226
1226
  const root = path.resolve(input.repoRoot);
1227
+ const statuses = [];
1227
1228
  for (const command of input.commands) {
1228
1229
  const cwd = path.resolve(command.cwd);
1229
1230
  if (!isWithinRepo(root, cwd) || !existsSync(cwd)) {
1230
1231
  throw new Error(`verification preflight rejected cwd for ${command.label}: ${command.cwd}`);
1231
1232
  }
1233
+ const vitestStatus = verifyVitestCommandPreflight({
1234
+ command,
1235
+ root,
1236
+ taskConfig: input.taskConfig,
1237
+ });
1238
+ if (vitestStatus) {
1239
+ statuses.push({ label: command.label, status: vitestStatus });
1240
+ continue;
1241
+ }
1232
1242
  const script = localScriptTarget(command);
1233
- if (!script)
1243
+ if (!script) {
1244
+ statuses.push({ label: command.label, status: "ok" });
1234
1245
  continue;
1246
+ }
1235
1247
  const scriptPath = path.resolve(cwd, script);
1236
- if (existsSync(scriptPath))
1248
+ if (existsSync(scriptPath)) {
1249
+ statuses.push({ label: command.label, status: "ok" });
1237
1250
  continue;
1251
+ }
1238
1252
  const writerMayCreate = input.taskConfig.allowedPaths.some((allowed) => pathMatchesPattern(script, allowed.replace(/^\.\//, "")));
1239
1253
  if (!writerMayCreate) {
1240
1254
  throw new Error(`verification preflight missing local script for ${command.label}: ${script}`);
1241
1255
  }
1256
+ statuses.push({ label: command.label, status: "future-delivery-script" });
1257
+ }
1258
+ return statuses;
1259
+ }
1260
+ function verifyVitestCommandPreflight(input) {
1261
+ const args = normalizeVerifyCommandArgs(input.command.args);
1262
+ const vitestIndex = args.findIndex((arg) => path.basename(arg).replace(/\.(?:exe|cmd)$/i, "") === "vitest");
1263
+ if (vitestIndex < 0)
1264
+ return undefined;
1265
+ const configArgIndex = args.findIndex((arg, index) => index > vitestIndex &&
1266
+ (arg === "--config" || arg === "-c" || arg.startsWith("--config=")));
1267
+ let configPath;
1268
+ if (configArgIndex >= 0) {
1269
+ const arg = args[configArgIndex];
1270
+ configPath = arg.startsWith("--config=")
1271
+ ? arg.slice("--config=".length)
1272
+ : args[configArgIndex + 1];
1273
+ }
1274
+ const cwd = path.resolve(input.command.cwd);
1275
+ const configAbsolute = configPath
1276
+ ? path.resolve(cwd, configPath)
1277
+ : findRootVitestConfig(input.root);
1278
+ const configRelative = configAbsolute
1279
+ ? path.relative(input.root, configAbsolute).replace(/\\/g, "/")
1280
+ : undefined;
1281
+ if (configAbsolute && !existsSync(configAbsolute)) {
1282
+ const writerMayCreate = Boolean(configRelative &&
1283
+ input.taskConfig.allowedPaths.some((allowed) => pathMatchesPattern(configRelative, allowed.replace(/^\.\//, ""))));
1284
+ if (!writerMayCreate) {
1285
+ throw new Error(`verification preflight missing Vitest config for ${input.command.label}: ${configRelative ?? configPath}`);
1286
+ }
1287
+ }
1288
+ const targetPaths = vitestTargetPaths(args.slice(vitestIndex + 1));
1289
+ if (targetPaths.length === 0)
1290
+ return "ok";
1291
+ const inheritedConfig = !configPath && configAbsolute && existsSync(configAbsolute);
1292
+ if (inheritedConfig && vitestConfigExcludesDogfood(configAbsolute, targetPaths)) {
1293
+ throw new Error(`verification preflight Vitest target ${targetPaths.join(", ")} is excluded by ${path.relative(input.root, configAbsolute).replace(/\\/g, "/")}`);
1294
+ }
1295
+ const futureTargets = targetPaths.filter((target) => {
1296
+ const absolute = path.resolve(cwd, target);
1297
+ if (existsSync(absolute))
1298
+ return false;
1299
+ return !input.taskConfig.allowedPaths.some((allowed) => pathMatchesPattern(target, allowed.replace(/^\.\//, "")));
1300
+ });
1301
+ if (futureTargets.length > 0) {
1302
+ throw new Error(`verification preflight missing Vitest target for ${input.command.label}: ${futureTargets.join(", ")}`);
1303
+ }
1304
+ const missingAllowedTargets = targetPaths.filter((target) => !existsSync(path.resolve(cwd, target)));
1305
+ return missingAllowedTargets.length > 0 ? "future-test-target" : "ok";
1306
+ }
1307
+ function findRootVitestConfig(root) {
1308
+ for (const name of ["vitest.config.ts", "vitest.config.js", "vitest.config.mjs", "vitest.config.cjs"]) {
1309
+ const candidate = path.join(root, name);
1310
+ if (existsSync(candidate))
1311
+ return candidate;
1312
+ }
1313
+ return undefined;
1314
+ }
1315
+ function vitestTargetPaths(args) {
1316
+ const targets = [];
1317
+ let afterRun = false;
1318
+ for (let index = 0; index < args.length; index += 1) {
1319
+ const arg = args[index];
1320
+ if (arg === "run" || arg === "related") {
1321
+ afterRun = true;
1322
+ continue;
1323
+ }
1324
+ if (!afterRun || arg.startsWith("-")) {
1325
+ if ((arg === "--config" || arg === "-c" || arg === "--reporter") && !arg.includes("="))
1326
+ index += 1;
1327
+ continue;
1328
+ }
1329
+ targets.push(arg);
1330
+ }
1331
+ return targets.filter((target) => target.includes("/") || /\.(?:test|spec)\.[^/]+$/.test(target));
1332
+ }
1333
+ function vitestConfigExcludesDogfood(configPath, targets) {
1334
+ if (!targets.some((target) => target.replace(/\\/g, "/").startsWith("dogfood/")))
1335
+ return false;
1336
+ try {
1337
+ const text = readFileSync(configPath, "utf8");
1338
+ return /exclude\s*:[\s\S]{0,500}dogfood\/\*\*/.test(text);
1339
+ }
1340
+ catch {
1341
+ return false;
1242
1342
  }
1243
1343
  }
1244
1344
  function isWithinRepo(root, candidate) {
@@ -1307,10 +1407,11 @@ function buildVerifyEvidence(input) {
1307
1407
  selectionReasons: selectedCommands?.map((command) => input.phase === "intermediate" && isFullSuiteVerifyCommand(command)
1308
1408
  ? `${command.label}: deferred-heavy`
1309
1409
  : `${command.label}: kept`) ?? [],
1310
- preflight: selectedCommands?.map((command) => ({
1311
- label: command.label,
1312
- status: "ok",
1313
- })) ?? [],
1410
+ preflight: input.preflight ??
1411
+ (selectedCommands?.map((command) => ({
1412
+ label: command.label,
1413
+ status: "ok",
1414
+ })) ?? []),
1314
1415
  commandTimeoutMs: input.commandTimeoutMs,
1315
1416
  totalTimeoutBudgetMs: commandCount * input.commandTimeoutMs,
1316
1417
  finalFullRequired: input.finalFullRequired,
@@ -1499,6 +1600,8 @@ function extractExplicitRequirementIds(requirementMarkdown, ...fallbackMarkdown)
1499
1600
  : markdown;
1500
1601
  for (const match of source.matchAll(/\b(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/gi)) {
1501
1602
  const id = match[0].toUpperCase();
1603
+ if (isPlaceholderRequirementId(id))
1604
+ continue;
1502
1605
  if (!seen.has(id)) {
1503
1606
  seen.add(id);
1504
1607
  ids.push(id);
@@ -1528,6 +1631,8 @@ function extractSectionRequirementIds(sectionMarkdown) {
1528
1631
  const seen = new Set();
1529
1632
  for (const match of sectionMarkdown.matchAll(/\b(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/gi)) {
1530
1633
  const id = match[0].toUpperCase();
1634
+ if (isPlaceholderRequirementId(id))
1635
+ continue;
1531
1636
  if (!seen.has(id)) {
1532
1637
  seen.add(id);
1533
1638
  ids.push(id);
@@ -1535,6 +1640,9 @@ function extractSectionRequirementIds(sectionMarkdown) {
1535
1640
  }
1536
1641
  return ids;
1537
1642
  }
1643
+ function isPlaceholderRequirementId(id) {
1644
+ return /(?:^|-)X{2,}$/.test(id);
1645
+ }
1538
1646
  /**
1539
1647
  * Prefer TaskSpec-scoped acceptance refs from the derived 需求.md section when
1540
1648
  * present. Full Feature acceptance.yaml may list sibling ACs that this task is
@@ -1613,21 +1721,14 @@ function buildDagSourceBinding(sources, taskKind) {
1613
1721
  const ledgerBinding = loadPersistedFrontendSourceBindingLedger(sources);
1614
1722
  if (!ledgerBinding)
1615
1723
  return base;
1616
- // AC-002 / AC-HARD-002:物化 REQ-SRC-* canonical id 确定性并入 v2 requirementIds。
1617
- // 生成型 REQ-SRC-* id 无法从 markdown 文本导出(必须来自持久化 ledger),此处
1618
- // 在 required requirement ids 侧确定性注入,确保 prewrite missing-requirement-ids
1619
- // 不误伤(不要求文本可导出)也不漏检(物化 id 必须被契约覆盖)。
1620
- const materializedRequirementIds = Object.keys(ledgerBinding.requirementToFragments)
1621
- .filter((id) => /^REQ-SRC-/.test(id) && !base.requirementIds.includes(id))
1622
- .sort();
1724
+ // v2 is ledger-owned: the persisted canonical set is the only requirement
1725
+ // identity source. In particular, explanatory AC-XXX mentions from a
1726
+ // materialized reference document must not leak in beside ledger ids.
1623
1727
  return {
1624
1728
  schemaVersion: 2,
1625
1729
  taskId: base.taskId,
1626
1730
  sources: base.sources,
1627
- requirementIds: [
1628
- ...base.requirementIds,
1629
- ...materializedRequirementIds,
1630
- ],
1731
+ requirementIds: ledgerBinding.requirementIds,
1631
1732
  ledgerPath: ledgerBinding.ledgerPath,
1632
1733
  ledgerSha256: ledgerBinding.ledgerSha256,
1633
1734
  inputDigest: ledgerBinding.inputDigest,
@@ -1644,15 +1745,19 @@ function buildDagSourceBinding(sources, taskKind) {
1644
1745
  */
1645
1746
  function loadPersistedFrontendSourceBindingLedger(sources) {
1646
1747
  const referenceDocs = (sources.referenceDocuments ?? []).filter((doc) => doc.markdown.trim().length > 0);
1647
- if (referenceDocs.length === 0)
1648
- return null;
1748
+ const ledgerAbsolutePath = path.join(sources.taskDir, "source", REQUIREMENT_LEDGER_FILE_NAME);
1749
+ // Text-created frontend tasks have no imported reference manifest. Their
1750
+ // task-owned projected requirement is nevertheless a valid canonical source
1751
+ // document and is persisted in the v2 ledger by source preparation. Keep it
1752
+ // in the current-input map below instead of silently downgrading to v1.
1649
1753
  // 存在性守卫(保持既有 v1 回退行为):派生视图无可抽取 requirement/acceptance
1650
1754
  // 引用时不要求持久化 ledger,直接回退 base binding。此守卫只用于"是否 ledger
1651
1755
  // 任务"判定,绝不参与 digest 键计算——键输入完全来自持久化 ledger 恢复。
1652
1756
  const guardFacts = extractRequirementFactsFromMarkdown(sources.requirementMarkdown);
1653
- if (guardFacts.acceptanceCriteria.length === 0)
1757
+ if (guardFacts.acceptanceCriteria.length === 0 && referenceDocs.length > 0)
1758
+ return null;
1759
+ if (referenceDocs.length === 0 && !existsSync(ledgerAbsolutePath))
1654
1760
  return null;
1655
- const ledgerAbsolutePath = path.join(sources.taskDir, "source", REQUIREMENT_LEDGER_FILE_NAME);
1656
1761
  let raw;
1657
1762
  try {
1658
1763
  raw = readFileSync(ledgerAbsolutePath, "utf8");
@@ -1668,6 +1773,13 @@ function loadPersistedFrontendSourceBindingLedger(sources) {
1668
1773
  catch (error) {
1669
1774
  throw new Error(`SOURCE_FIDELITY_LEDGER_MISSING: persisted ${REQUIREMENT_LEDGER_FILE_NAME} is not a valid v1 ledger: ${error instanceof Error ? error.message : String(error)}`);
1670
1775
  }
1776
+ const ledgerErrors = validateRequirementLedger(ledger);
1777
+ if (ledgerErrors.length > 0) {
1778
+ const first = ledgerErrors[0];
1779
+ throw new Error(`${first.code}: persisted ${REQUIREMENT_LEDGER_FILE_NAME} is invalid; refusing to start a frontend DAG with incomplete source bindings (${ledgerErrors
1780
+ .map((error) => error.message)
1781
+ .join("; ")})`);
1782
+ }
1671
1783
  // canonical 侧:ledger.canonicalRequirements 按 build 顺序原样保留输入 canonical
1672
1784
  // requirements(id/text 逐字节),过滤物化 REQ-SRC-*(AC-HARD-002 "物化不入键")
1673
1785
  // 即精确恢复 digest 输入集;派生导航视图 需求.md 投影不参与键计算。
@@ -1686,6 +1798,13 @@ function loadPersistedFrontendSourceBindingLedger(sources) {
1686
1798
  for (const doc of referenceDocs) {
1687
1799
  currentByPath.set(toDagSourcePath(sources, doc.path), doc.markdown);
1688
1800
  }
1801
+ if (referenceDocs.length === 0) {
1802
+ currentByPath.set(toDagSourcePath(sources, sources.requirementPath),
1803
+ // applyTaskContract appends a derived `## Source ledger` navigation
1804
+ // block after the ledger input has been hashed. Remove that block when
1805
+ // reconstructing the canonical text digest.
1806
+ sources.requirementMarkdown.replace(/\n## Source ledger\b[\s\S]*$/u, ""));
1807
+ }
1689
1808
  const missingAuthoritativePaths = contract.sourcePaths.filter((sourcePath) => !currentByPath.has(sourcePath));
1690
1809
  if (missingAuthoritativePaths.length > 0) {
1691
1810
  throw new Error(`SOURCE_FIDELITY_LEDGER_STALE: persisted ${REQUIREMENT_LEDGER_FILE_NAME} was built from authoritative source documents no longer present (${missingAuthoritativePaths.join(", ")}); re-run task advance (intake) to rebuild the ledger`);
@@ -1713,6 +1832,7 @@ function loadPersistedFrontendSourceBindingLedger(sources) {
1713
1832
  ledgerPath: toDagSourcePath(sources, ledgerAbsolutePath),
1714
1833
  ledgerSha256,
1715
1834
  inputDigest: ledger.inputDigest,
1835
+ requirementIds: ledger.canonicalRequirements.map((requirement) => requirement.id),
1716
1836
  requirementToFragments,
1717
1837
  };
1718
1838
  }
@@ -1845,6 +1965,7 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
1845
1965
  const manifest = await loadHarnessManifest(repoRoot);
1846
1966
  const strategy = resolveDagVerifyStrategy(taskConfig);
1847
1967
  let verifyCommands;
1968
+ let verificationPreflight = [];
1848
1969
  try {
1849
1970
  const adapter = await resolveAdapter(repoRoot);
1850
1971
  const preset = resolveVerifyPreset(taskConfig.verifyPreset, taskConfig);
@@ -1868,7 +1989,7 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
1868
1989
  ...taskVerifyCommands(repoRoot, taskConfig),
1869
1990
  ...adapterFinal,
1870
1991
  ]);
1871
- assertVerificationPlanPreflight({
1992
+ verificationPreflight = assertVerificationPlanPreflight({
1872
1993
  repoRoot,
1873
1994
  commands: [...intermediatePlan.commands, ...finalPlan.commands],
1874
1995
  taskConfig,
@@ -1911,6 +2032,7 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
1911
2032
  enabledExecutors: resolveEnabledExecutors(manifest.executors),
1912
2033
  executorModelMatrix: resolveExecutorModelMatrices(manifest),
1913
2034
  verifyCommands,
2035
+ verificationPreflight,
1914
2036
  sddEmbeddedSkills: await probeRepoLocalSddSkills(repoRoot),
1915
2037
  projectGovernancePresent: await discoverProjectGovernancePresence(repoRoot),
1916
2038
  ...(frontendShapeTransitionCapsule ? { autoloadedFrontendShapeTransitionCapsule: frontendShapeTransitionCapsule } : {}),
@@ -2004,6 +2126,7 @@ export function buildStandardHybridDagFromTask(sources) {
2004
2126
  finalFullRequired: true,
2005
2127
  commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
2006
2128
  plan: finalVerifyPlan,
2129
+ preflight: sources.verificationPreflight,
2007
2130
  }),
2008
2131
  cwd: ".",
2009
2132
  timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
@@ -2331,6 +2454,7 @@ function buildFrontendMockVerifyNode(sources, implementId, readOnlyPaths, forbid
2331
2454
  fallbackCommands: [],
2332
2455
  commandTexts: commands,
2333
2456
  commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
2457
+ preflight: sources.verificationPreflight,
2334
2458
  }),
2335
2459
  cwd: ".",
2336
2460
  timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
@@ -3043,6 +3167,7 @@ async function buildFrontendHybridDagFromTask(sources) {
3043
3167
  fallbackCommands: staticFallbackCommands,
3044
3168
  commandTexts: staticShellCommands,
3045
3169
  commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
3170
+ preflight: sources.verificationPreflight,
3046
3171
  });
3047
3172
  const lintVerifyEvidence = lintShellCommands.length > 0
3048
3173
  ? buildVerifyEvidence({
@@ -3053,6 +3178,7 @@ async function buildFrontendHybridDagFromTask(sources) {
3053
3178
  fallbackCommands: [],
3054
3179
  commandTexts: lintShellCommands,
3055
3180
  commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
3181
+ preflight: sources.verificationPreflight,
3056
3182
  })
3057
3183
  : undefined;
3058
3184
  const behaviorVerifyEvidence = buildVerifyEvidence({
@@ -3064,6 +3190,7 @@ async function buildFrontendHybridDagFromTask(sources) {
3064
3190
  commandTexts: behaviorShellCommands,
3065
3191
  finalFullRequired: true,
3066
3192
  commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
3193
+ preflight: sources.verificationPreflight,
3067
3194
  });
3068
3195
  const mockVerifyTemplate = mockMode === "required" && hasMockVerifyCommands
3069
3196
  ? buildFrontendMockVerifyNode(frontendSources, implementId, readOnlyPaths, forbiddenPaths)
@@ -3227,7 +3354,7 @@ async function buildFrontendHybridDagFromTask(sources) {
3227
3354
  subtask_prompt: [
3228
3355
  "Inspect frontend code, routing, components, styles, package scripts, and tests.",
3229
3356
  "Return code and design observations, existing reuse opportunities, and verification entry points.",
3230
- "Report target surface and design evidence as facts (no fixed section title required): completeness, entrypoint, routeOrMount, implementationPaths, testPaths, dataSource, allowedPathConflicts, unresolvedPaths. Use repository-relative POSIX paths. A complete surface must name at least one proven entrypoint, implementation path, or test path and set unresolvedPaths to []; if any target ownership remains unknown, commit completeness=blocked with every unresolved path instead of guessing. implementationPaths and testPaths must name the existing files/directories that actually own the requested behavior; allowedPathConflicts must list every discovered path not covered by task allowedPaths, or [] when none exists.",
3357
+ "Report target surface and design evidence as facts (no fixed section title required): completeness, entrypoint, routeOrMount, implementationPaths, testPaths, dataSource, allowedPathConflicts, unresolvedPaths. Use repository-relative POSIX paths. A complete surface must name at least one target candidate and set unresolvedPaths to []; if any target ownership remains unknown, commit completeness=blocked with every unresolved path instead of guessing. Prefer existing files/directories when they exist. For a greenfield target explicitly pinned by the task source, future implementation/test paths are allowed, but every such path must be named by the source and runtime-enriched as sourceDeclared; do not invent paths merely because they fit allowedPaths. allowedPathConflicts must list every discovered path not covered by task allowedPaths, or [] when none exists.",
3231
3358
  "Derive all file paths from this target workspace. Do not assume the project uses src/, test/, React, or the loop-agent repository layout.",
3232
3359
  "Read-only: do not modify repository files.",
3233
3360
  sourceContexts.scout,
@@ -3422,6 +3549,7 @@ async function buildFrontendHybridDagFromTask(sources) {
3422
3549
  outputContract: "The implementation status is derived by the executor from mechanical facts (write-tool events, run delta, write guard, requirement coverage, focused-check), not from any IMPLEMENTATION_OUTCOME first line. Deliver a Markdown summary with Contract Ref (path/schema/hash), Changed Files, Requirements Implemented, UI States, Tests Changed, Verification Attempts, Deviations, and Residual Risks. Follow fixed stages: contract confirm → tests → component/state → API/Mock → focused checks → diff cleanup.",
3423
3550
  subtask_prompt: [
3424
3551
  "Implement against the validated run-owned Frontend Implementation Contract materialized by frontend-design-policy-shell (path/schema/hash) and authorized by frontend-writer-admission-shell. Do not rebuild the contract from Markdown alone.",
3552
+ "WRITER TOOL PROTOCOL (hard): the response text is not delivery. Never paste source code, test code, or full file contents into chat. After reading the canonical contract, make the first implementation change with the structured write/edit tool (one file per call); continue writing through those tools until the writeSet is complete. If a write/edit tool is unavailable, stop and report the blocked capability instead of drafting code in the response.",
3425
3553
  "The canonical contract already contains the approved requirement, target-file, UI-state, verification, design, and Mock/API decisions. Do not re-open task sources, OpenSpec, AI workspace, plan/revision, or design-review prose, and do not repeat broad repository research. Inspect only contract target files and directly related local code needed to implement them.",
3426
3554
  "Execute in fixed stages and report each in the delivery summary: (1) Contract confirm, (2) Tests sync, (3) Component/UI state implementation, (4) API/Mock wiring per contract.mockApi, (5) Focused checks behind frozen entrypoints only, (6) Diff cleanup.",
3427
3555
  "Map every requirement id, expectedOutcome, interaction trigger/expectedBehavior, and applicable UI state from the contract to concrete files. Do not invent shell verification commands; only frozen static/behavior entrypoints will run.",
@@ -4247,7 +4375,27 @@ const headingAlias={'\u6a21\u5757\u7d22\u5f15':'Module Index','\u8986\u76d6\u830
4247
4375
  const allLines=readme.replace(/\\r\\n/g,'\\n').replace(/\\r/g,'\\n').split('\\n');
4248
4376
  let headingRepair=false;
4249
4377
  for(let i=0;i<allLines.length;i++){const alias=/^##\\s+(模块索引|覆盖范围|覆盖矩阵|场景分区)\\s*$/.exec(allLines[i].trim());if(alias){allLines[i]='## '+headingAlias[alias[1]];headingRepair=true;}}
4250
- if(headingRepair)readme=allLines.join('\\n');
4378
+ let partitionRepair=false;
4379
+ const partHeadTrim=[];for(let i=0;i<allLines.length;i++){if(allLines[i].trim()==='## Scenario Partitions')partHeadTrim.push(i);}
4380
+ if(partHeadTrim.length===1){
4381
+ const pStart=partHeadTrim[0]+1;let pEnd=allLines.length;for(let i=pStart;i<allLines.length;i++){if(/^##\\s+\\S/.test(allLines[i].trim())){pEnd=i;break;}}
4382
+ const pCells=line=>line.split('|').slice(1,-1).map(v=>String(v||'').trim());
4383
+ const pHeader=['Partition ID','Operation','Axis','Domain','Required Slots','Expected by Slot','Bind Rule'];
4384
+ for(let i=pStart;i<pEnd;i++){
4385
+ if(!allLines[i].includes('|')) continue;
4386
+ const row=pCells(allLines[i]);
4387
+ if(row.length<=7) continue;
4388
+ if(pHeader.every((v,idx)=>row[idx]===v)) continue;
4389
+ const pid=String(row[0]||'').trim();
4390
+ const extras=row.slice(7).join(';').split(/[;,,;]/).map(v=>v.trim().split(bt).join('')).filter(Boolean);
4391
+ const prefix='TP-'+pid.toUpperCase()+'-';
4392
+ if(extras.length && extras.every(t=>/^TP-[A-Z0-9-]+$/i.test(t)&&(t.toUpperCase().startsWith(prefix)||t.toUpperCase().startsWith('TP-SP-')))){
4393
+ allLines[i]='| '+row.slice(0,7).join(' | ')+' |';
4394
+ partitionRepair=true;
4395
+ }
4396
+ }
4397
+ }
4398
+ if(headingRepair||partitionRepair)readme=allLines.join('\\n');
4251
4399
  const headings=[];for(let i=0;i<allLines.length;i++){if(allLines[i].trim()==='## Module Index')headings.push(i);}
4252
4400
  if(headings.length!==1){process.stderr.write((headings.length===0?'missing-module-index':'duplicate-module-index')+'; require exactly one exact ## Module Index section\\n');process.exit(2);}
4253
4401
  const start=headings[0]+1;let end=allLines.length;for(let i=start;i<allLines.length;i++){if(/^##\\s+\\S/.test(allLines[i].trim())){end=i;break;}}
@@ -4260,7 +4408,7 @@ const headerIndex=lines.findIndex(line=>{const row=cells(line);return expectedHe
4260
4408
  if(headerIndex<0){process.stderr.write('invalid-module-index-header: require exact business ownership and path columns\\n');process.exit(2);}
4261
4409
  const dataRows=lines.slice(headerIndex+2).map(cells).filter(row=>row.length>=8&&row[0]&&row[0]!=='Module Stem');
4262
4410
  const allowedSplit=new Set(['explicit-user-layout','primary-business-resource','independent-business-resource','output-budget']);
4263
- const operationOwners=new Map();const canonicalSeen=new Set();const declaredModules=[];let planRepairApplied=headingRepair;
4411
+ const operationOwners=new Map();const canonicalSeen=new Set();const declaredModules=[];let planRepairApplied=headingRepair||partitionRepair;
4264
4412
  for(const row of dataRows){
4265
4413
  const rawStem=String(row[0]||'').trim(),resource=String(row[1]||'').trim(),operations=String(row[2]||'').split(';').map(value=>value.trim()).filter(Boolean),split=String(row[5]||'').trim();
4266
4414
  const mdMatches=[...String(row[6]||'').matchAll(rxMdPath)];
@@ -4302,7 +4450,7 @@ if(planRepairApplied){
4302
4450
  const table=['| '+expectedHeader.join(' | ')+' |','|'+expectedHeader.map(()=>'---').join('|')+'|',...declaredModules.map(item=>'| '+[item.stem,item.businessResource,item.ownedOperations.join('; '),item.ownedRuleKeys.join('; '),item.caseIds.join('; '),item.splitReason,'['+item.stem+'](./'+item.stem+'.md) '+item.markdownPath,item.pytestPath].join(' | ')+' |')].join('\\n');
4303
4451
  readme=[...allLines.slice(0,headings[0]+1),table,...allLines.slice(end)].join('\\n');
4304
4452
  fs.writeFileSync(planPath,readme,'utf8');
4305
- } else if(headingRepair){
4453
+ } else if(headingRepair||partitionRepair){
4306
4454
  fs.writeFileSync(planPath,readme,'utf8');
4307
4455
  }
4308
4456
  for(const [operation,owners] of operationOwners){if(owners.length>1&&!owners.every(owner=>owner.split==='explicit-user-layout'||owner.split==='output-budget')){process.stderr.write('overlapping-operation-modules: '+operation+' -> '+owners.map(owner=>owner.stem).join(',')+'; merge by business resource or use an authoritative explicit-user-layout\\n');process.exit(2);}}
@@ -838,7 +838,7 @@ export function buildAttemptPrompt(task, basePrompt, attemptNumber, previousFail
838
838
  "",
839
839
  "<retry_instruction>",
840
840
  "The previous Scout attempt did not commit a complete, runtime-evidenced target surface.",
841
- "Search the repository only as needed to establish the real entrypoint, implementation ownership, and applicable test path. Commit record_target_surface with completeness=complete and unresolvedPaths=[] only after at least one named target has fresh runtime evidence. If ownership truly cannot be established, record completeness=blocked with each unresolved path; do not make Plan discover it.",
841
+ "Search the repository only as needed to establish the real entrypoint, implementation ownership, and applicable test path. Commit record_target_surface with completeness=complete and unresolvedPaths=[] only after at least one named target has fresh runtime evidence, unless the task source explicitly declares a greenfield target: in that case every future path must be source-declared by the runtime-enriched fact. If ownership truly cannot be established, record completeness=blocked with each unresolved path; do not make Plan discover it.",
842
842
  "</retry_instruction>",
843
843
  ].join("\n");
844
844
  }
@@ -87,7 +87,7 @@ export const dagShellVerifyEvidenceSchema = z.object({
87
87
  preflight: z
88
88
  .array(z.object({
89
89
  label: z.string(),
90
- status: z.enum(["ok", "future-delivery-script"]),
90
+ status: z.enum(["ok", "future-delivery-script", "future-test-target"]),
91
91
  }))
92
92
  .default([]),
93
93
  /** Effective timeout applied independently to each shell command. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.42.0-next.6",
3
+ "version": "0.42.0-next.8",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",
@@ -56,10 +56,11 @@
56
56
  "console:build": "npm run console:typecheck && vite build --config src/worker/console/vite.config.ts",
57
57
  "console:dev": "node --import tsx/esm scripts/console-dev.mjs",
58
58
  "prepack": "npm run build",
59
- "prepublishOnly": "node scripts/check-npm-publish-policy.mjs && npm run typecheck && npm test && npm run build",
59
+ "prepublishOnly": "node scripts/check-npm-publish-policy.mjs && npm run typecheck && npm run test:full && npm run build",
60
60
  "lint": "npm run typecheck",
61
61
  "typecheck": "tsc --noEmit && npm run console:typecheck",
62
62
  "test": "node scripts/run-tests.mjs",
63
+ "test:full": "node scripts/run-tests.mjs --full",
63
64
  "test:host": "vitest run test/init-upgrade.test.ts --maxWorkers=1 -t \"records and reuses|controller-authorized semantic merge|authority-sensitive\"",
64
65
  "test:fast": "vitest run --config vitest.fast.config.ts",
65
66
  "test:io-heavy": "vitest run --config vitest.io-heavy.config.ts",
@@ -1 +0,0 @@
1
- import{ag as o,ah as n}from"./mermaid.core-CwLZPtQo.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-DauNE69H.js";import{_ as i}from"./mermaid.core-CwLZPtQo.js";import"./chunk-5VM5RSS4-D1kLrlU_.js";import"./chunk-XXDRQBXY-BJYr4xHC.js";import"./chunk-KBJHAD2P-BuILuoxs.js";import"./chunk-2GRJ4B5K-q-0yjQ1h.js";import"./index-CVdddQsA.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-DauNE69H.js";import{_ as i}from"./mermaid.core-CwLZPtQo.js";import"./chunk-5VM5RSS4-D1kLrlU_.js";import"./chunk-XXDRQBXY-BJYr4xHC.js";import"./chunk-KBJHAD2P-BuILuoxs.js";import"./chunk-2GRJ4B5K-q-0yjQ1h.js";import"./index-CVdddQsA.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};