@tea-agent/loop-agent 0.38.1 → 0.38.2

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 (127) hide show
  1. package/CHANGELOG.md +109 -13
  2. package/dist/application/dag/generate-task-dag.js +9 -0
  3. package/dist/build-stamp.json +3 -3
  4. package/dist/commands/init.js +2 -14
  5. package/dist/executors/dag-pi-executor.js +41 -4
  6. package/dist/executors/shell-executor.js +269 -15
  7. package/dist/governance/manifest-types.js +0 -2
  8. package/dist/shared/operator/capabilities.js +87 -22
  9. package/dist/task/config-types.js +22 -0
  10. package/dist/worker/console/chat/deferred-turn.js +91 -0
  11. package/dist/worker/console/chat/pi-runtime.js +10 -5
  12. package/dist/worker/console/chat/routes.js +458 -47
  13. package/dist/worker/console/chat/semantic-activity.js +13 -13
  14. package/dist/worker/console/chat/session-store.js +146 -2
  15. package/dist/worker/console/harness-pi-model-matrix.js +2 -11
  16. package/dist/worker/console/operator-actions.js +5 -4
  17. package/dist/worker/console/pi-readiness.js +1 -0
  18. package/dist/worker/console/recovery-cta.js +4 -4
  19. package/dist/worker/console/recovery-error-copy.js +4 -4
  20. package/dist/worker/console/server.js +6 -1
  21. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Hfsot6rP.js → abnfDiagram-N423BO3Z-C4bmo3iY.js} +1 -1
  22. package/dist/worker/console/static/assets/{arc-YGwEASin.js → arc-SAD7McOS.js} +1 -1
  23. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CmOM0F92.js → architectureDiagram-T3A2C74G-DAVLUdK1.js} +1 -1
  24. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-DDLuaHC5.js → blockDiagram-VBNYF7ZC-CPPdv4vj.js} +1 -1
  25. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B0Um43W4.js → c4Diagram-5PPSVZJV-D1GRGZax.js} +1 -1
  26. package/dist/worker/console/static/assets/channel-R6oQIDkw.js +1 -0
  27. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DvBN4JZc.js → chunk-2GRJ4B5K-D_Bq2ZqK.js} +1 -1
  28. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-B1UVgW4E.js → chunk-2Q5K7J3B-CPLVtamK.js} +1 -1
  29. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Cf0GgXzX.js → chunk-5RXB4S5H-DnaZWcL3.js} +1 -1
  30. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DbNA24sj.js → chunk-5VM5RSS4-BRTmFgnq.js} +1 -1
  31. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Bi_0Be06.js → chunk-6Q2QTUOP-BRlfYZoH.js} +1 -1
  32. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C8pPGvRd.js → chunk-GF5L2VYU-CjNybD4u.js} +1 -1
  33. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BitQY8Z-.js → chunk-JWPE2WC7-D3e-74fG.js} +1 -1
  34. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-U67GCiEo.js → chunk-KBJHAD2P-Br3ir6f2.js} +1 -1
  35. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-IoJwXPBA.js → chunk-RYQCIY6F-1NgVuRbh.js} +1 -1
  36. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-YDZ_UHgE.js → chunk-XXDRQBXY-BTYuvuWN.js} +1 -1
  37. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BPXlmPWI.js +1 -0
  38. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BPXlmPWI.js +1 -0
  39. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CUZgsiRC.js → cose-bilkent-JH36ORCC-BC6Bgosf.js} +1 -1
  40. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-COmCh4pT.js → cynefin-VYW2F7L2-DV-HypfL.js} +1 -1
  41. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Ta6FSm68.js → cynefinDiagram-MW4NZA55-DhTaddO-.js} +1 -1
  42. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-UdcR-VW9.js → dagre-VZM6K2ZE-BnEcOmiI.js} +1 -1
  43. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DybcO3Et.js → diagram-7IWD3JNH-xphGRVKp.js} +1 -1
  44. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-wErDFWLw.js → diagram-B4RE2ZJO-CrdD1mRG.js} +1 -1
  45. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DkVHZWqz.js → diagram-LBJQPF4R-D4YdLsaF.js} +1 -1
  46. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DdqWs7QN.js → diagram-Q27KOJAE-LCKyGZ5U.js} +1 -1
  47. package/dist/worker/console/static/assets/{diagram-UB23O5K3-C9dMVmDo.js → diagram-UB23O5K3-BeiTe7t0.js} +1 -1
  48. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-B2T2WcBK.js → ebnfDiagram-BXEA7PRR-BO2cq8GS.js} +1 -1
  49. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bt9rjDFq.js → erDiagram-JOGREHBK-CmWww3Nt.js} +1 -1
  50. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-U8ic_tvi.js → flowDiagram-UKHOOZJN-C2kkUYg5.js} +1 -1
  51. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BfQJdQoO.js → ganttDiagram-PKOTCBZU-DY_JZ2iQ.js} +1 -1
  52. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-CpMftH4O.js → gitGraphDiagram-DS77QQ5N-BizhCSw9.js} +1 -1
  53. package/dist/worker/console/static/assets/index-BXN89VPX.js +325 -0
  54. package/dist/worker/console/static/assets/index-CuH5CNFu.css +1 -0
  55. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DzqWXNO_.js → infoDiagram-6WML65LV-Dlo2eK_d.js} +1 -1
  56. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CJmGQ5Yp.js → ishikawaDiagram-WSZJBQD7-3EIlIIuZ.js} +1 -1
  57. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-ChPtKFLz.js → journeyDiagram-NVQOT4AX-DiMDe5Yh.js} +1 -1
  58. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-PRe7wB5u.js → kanban-definition-27J2QSJJ-sN553oIN.js} +1 -1
  59. package/dist/worker/console/static/assets/{linear-DpKX6QNp.js → linear-D9Py31Ld.js} +1 -1
  60. package/dist/worker/console/static/assets/{mermaid.core-Dnl76b7Y.js → mermaid.core-D7Occzk_.js} +5 -5
  61. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-S9PCXc3J.js → mindmap-definition-FAOFIHXS-DjDRboiR.js} +1 -1
  62. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Du8e-FSX.js → pegDiagram-VL7TDLO6-Cgc4qlzp.js} +1 -1
  63. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CY6OmHTc.js → pieDiagram-7S7Q4E2Y-DxZj8pKd.js} +1 -1
  64. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C65GPhpj.js → quadrantDiagram-CIZ2JOQS-CJ50MngR.js} +1 -1
  65. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-U6IUiTCL.js → railroadDiagram-AXF67PYL-BY-OD8Tr.js} +1 -1
  66. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D-I_9Qhi.js → requirementDiagram-LRYGKXZP-CQFButfK.js} +1 -1
  67. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B57Fhed0.js → sankeyDiagram-W5VNT64P-BB_QUK_b.js} +1 -1
  68. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CJOtiw21.js → sequenceDiagram-SI44F4Z6-CqhXZjyx.js} +1 -1
  69. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-FyX8zxSh.js → sizeCapture-X5ZJPWSS-2l1U4rgJ.js} +1 -1
  70. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-eF-pbs9t.js → stateDiagram-OKZ733FA-BX1FbrhJ.js} +1 -1
  71. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DJKi0Qel.js +1 -0
  72. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CcfkA-Dq.js → swimlanes-SLNWSIFB-DOSnT7hb.js} +2 -2
  73. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-YYe7EEm5.js +8 -0
  74. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DH8FjnOK.js → timeline-definition-Z64GVDOM-DVJQA7AN.js} +1 -1
  75. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-beoTagJg.js → vennDiagram-T6HMQDX7-74uxl9gx.js} +1 -1
  76. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CQSfw4cK.js → wardleyDiagram-T6FBY63Y-DsBxlcg_.js} +1 -1
  77. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CQgzQ6jn.js → xychartDiagram-ELKLHX3M-snuW9Mjw.js} +1 -1
  78. package/dist/worker/console/static/index.html +2 -2
  79. package/dist/worker/console/static-src/app/console-types.js +0 -12
  80. package/dist/worker/console/static-src/app/useOperatorActions.js +1 -1
  81. package/dist/worker/console/static-src/app/useRecoveryActions.js +2 -2
  82. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +38 -1
  83. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +8 -1
  84. package/dist/worker/console/static-src/operator-chat/turn-submission.js +42 -0
  85. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +3 -0
  86. package/dist/worker/console/static-src/operator-chat/useChatStream.js +204 -11
  87. package/dist/worker/console/static-src/operator-chat/useChatThread.js +23 -1
  88. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +755 -51
  89. package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
  90. package/dist/workflows/dag/backend-test-execution-contract.js +18 -9
  91. package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
  92. package/dist/workflows/dag/backend-test-intake-context.js +32 -0
  93. package/dist/workflows/dag/backend-test-layout.js +133 -0
  94. package/dist/workflows/dag/backend-test-markdown-workflow.js +167 -32
  95. package/dist/workflows/dag/backend-test-module-stem.js +5 -0
  96. package/dist/workflows/dag/backend-test-pytest-collection.js +368 -33
  97. package/dist/workflows/dag/backend-test-result-contract.js +1 -1
  98. package/dist/workflows/dag/backend-test-scenario-param.js +884 -140
  99. package/dist/workflows/dag/backend-test-scenario-partitions.js +249 -0
  100. package/dist/workflows/dag/backend-test-writer-completeness.js +100 -56
  101. package/dist/workflows/dag/contract-validator-registrations.js +2 -1
  102. package/dist/workflows/dag/convergence/controller.js +141 -0
  103. package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
  104. package/dist/workflows/dag/dynamic-runtime/shared.js +1 -1
  105. package/dist/workflows/dag/frontend-implementation-contract.js +236 -8
  106. package/dist/workflows/dag/frontend-prewrite-gate.js +95 -35
  107. package/dist/workflows/dag/init-hybrid.js +652 -87
  108. package/dist/workflows/dag/node-execution.js +45 -18
  109. package/dist/workflows/dag/prompt.js +45 -0
  110. package/dist/workflows/dag/rerun-feedback.js +212 -0
  111. package/dist/workflows/dag/rerun-plan.js +93 -27
  112. package/dist/workflows/dag/rerun-run.js +3 -0
  113. package/dist/workflows/dag/rerun-task.js +61 -2
  114. package/dist/workflows/dag/retry-policy.js +18 -0
  115. package/dist/workflows/dag/types.js +98 -0
  116. package/docs/templates/backend-test-dag.json +60 -35
  117. package/docs/templates/harness.schema.json +0 -8
  118. package/harness.json +1 -8
  119. package/package.json +1 -1
  120. package/skills/frontend-implementation/references/node-contracts.md +2 -2
  121. package/dist/worker/console/static/assets/channel-Dp_2Aqf1.js +0 -1
  122. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL--CODy37j.js +0 -1
  123. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE--CODy37j.js +0 -1
  124. package/dist/worker/console/static/assets/index-BA5-N5ER.css +0 -1
  125. package/dist/worker/console/static/assets/index-DKPXBrdc.js +0 -325
  126. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B-mvnLEc.js +0 -1
  127. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-JtpBmIv4.js +0 -8
@@ -8,6 +8,7 @@ import { analyzeFrontendImplementationContract, writeFrontendImplementationContr
8
8
  import { renderFrontendPlanMarkdown } from "./frontend-plan-render.js";
9
9
  import { captureFrontendWorktreeBaseline } from "./frontend-worktree-diff.js";
10
10
  import { pathMatchesPattern } from "../../shared/git-progress.js";
11
+ import { normalizeVerdictCandidateLine } from "./dynamic-runtime/shared.js";
11
12
  export const FRONTEND_PREWRITE_RESULT_SCHEMA_ID = "frontend-prewrite-result-v1";
12
13
  export const FRONTEND_PREWRITE_RESULT_ARTIFACT_NAME = "frontend-prewrite-result.json";
13
14
  /** Canonical contract serialization must stay at or below this byte size. */
@@ -39,6 +40,7 @@ export const frontendPrewriteFailureCodeSchema = z.enum([
39
40
  "component-spec-not-cited",
40
41
  "component-choices-missing",
41
42
  "contract-too-large",
43
+ "revision-patch-unmergeable",
42
44
  ]);
43
45
  export const frontendPrewriteResultV1Schema = z
44
46
  .object({
@@ -160,9 +162,12 @@ async function readNodeText(runDir, nodeId) {
160
162
  if (record.status !== "FINISHED") {
161
163
  throw new Error(`frontend prewrite gate requires FINISHED node ${nodeId} (got ${String(record.status)})`);
162
164
  }
163
- const text = [record.assistantText, record.stdout]
164
- .filter((value) => Boolean(value?.trim()))
165
- .join("\n");
165
+ // Pi execution mirrors a successful structured-contract normalization into
166
+ // both assistantText and stdout. Treat node output the same way as the DAG
167
+ // runtime's canonicalNodeOutput(): assistantText is authoritative and stdout
168
+ // is only a fallback. Concatenating the two duplicate fields makes a single
169
+ // canonical plan appear as two complete JSON contracts to the prewrite gate.
170
+ const text = record.assistantText?.trim() || record.stdout?.trim() || "";
166
171
  if (!text)
167
172
  throw new Error(`frontend prewrite gate empty output from ${nodeId}`);
168
173
  return text;
@@ -171,16 +176,53 @@ async function readNodeRawText(runDir, nodeId) {
171
176
  const record = JSON.parse(await readFile(path.join(runDir, `${nodeId}.json`), "utf8"));
172
177
  return record.assistantText ?? record.stdout ?? "";
173
178
  }
179
+ /**
180
+ * Same shape predicate as the internal `isContract` inside
181
+ * `extractFrontendImplementationJson` (frontend-implementation-contract.ts):
182
+ * schemaVersion 1 + a plain `targets` object + `requirements` and
183
+ * `verificationTargets` arrays. A merge-patch delta does not carry these
184
+ * fields, so this is the deterministic boundary between a full contract and a
185
+ * patch. Keep the two predicates in sync.
186
+ */
187
+ function isFullContractShape(value) {
188
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
189
+ return false;
190
+ }
191
+ const record = value;
192
+ const targets = record.targets;
193
+ return (record.schemaVersion === 1 &&
194
+ targets !== null &&
195
+ typeof targets === "object" &&
196
+ !Array.isArray(targets) &&
197
+ Array.isArray(record.requirements) &&
198
+ Array.isArray(record.verificationTargets));
199
+ }
200
+ /**
201
+ * Shared merge pipeline for revisionPatch mode and content-driven
202
+ * compatibility: read the original contract node, extract the original
203
+ * contract and the effective node's patch, apply the RFC 7386 merge-patch, and
204
+ * serialize deterministically. Throws on any failure; callers map the error to
205
+ * their own classification (retryable-invalid for explicit revisionPatch mode,
206
+ * blocked for content-driven fallback).
207
+ */
208
+ async function mergeRevisionPatch(runDir, planText, originalNodeId) {
209
+ const originalText = await readNodeText(runDir, originalNodeId);
210
+ const originalValue = extractFrontendImplementationJson(originalText);
211
+ const patchValue = extractFrontendImplementationJson(planText);
212
+ const merged = applyFrontendImplementationContractPatch(originalValue, patchValue);
213
+ return serializeDeterministicJson(merged);
214
+ }
174
215
  function firstNonEmptyVerdictLine(text) {
175
216
  const lines = text
176
217
  .split(/\r?\n/)
177
218
  .map((line) => line.trim())
178
219
  .filter(Boolean);
179
- const normalize = (line) => {
180
- const emphasized = line.match(/^(?:`{1,3}|\*{1,3})\s*(VERDICT:[^`*]+?)\s*(?:`{1,3}|\*{1,3})$/);
181
- return (emphasized?.[1] ?? line).trim();
182
- };
183
- const normalizedLines = lines.map(normalize);
220
+ // Emphasis stripping (backtick + asterisk) is shared with the runIf
221
+ // selector and node-level output-protocol validation via
222
+ // normalizeVerdictCandidateLine. The gate keeps its own loose
223
+ // pass/request-revision normalization below (prefix match + trailing
224
+ // prose tolerance) for deterministic fail-closed behavior.
225
+ const normalizedLines = lines.map(normalizeVerdictCandidateLine);
184
226
  const first = normalizedLines[0] ?? "";
185
227
  // The prompt requires VERDICT to be the first non-empty line, but model
186
228
  // output can still prepend a summary. Keep the protocol strict in the
@@ -459,9 +501,8 @@ export async function runFrontendPrewriteGate(input) {
459
501
  let effectiveContractText = null;
460
502
  if (revisionPatchMode) {
461
503
  const originalNodeId = input.config.planFallbackFromNodeIds[0];
462
- let originalText;
463
504
  try {
464
- originalText = await readNodeText(input.runDir, originalNodeId);
505
+ effectiveContractText = await mergeRevisionPatch(input.runDir, planText, originalNodeId);
465
506
  }
466
507
  catch (error) {
467
508
  return finalizePrewrite(input, {
@@ -472,35 +513,54 @@ export async function runFrontendPrewriteGate(input) {
472
513
  failureCode: "contract-invalid",
473
514
  });
474
515
  }
475
- let originalValue;
476
- let patchValue;
516
+ }
517
+ else {
518
+ // Content-driven compatibility (Bug 1): legacy DAGs without
519
+ // revisionPatch can still receive a merge-patch delta from the revision
520
+ // node. Detect the output shape instead of trusting the DAG flag alone.
521
+ // - Full contract shape → keep legacy full-contract semantics unchanged.
522
+ // - Patch shape with a usable original contract node → auto-merge and
523
+ // run the existing analysis/materialization pipeline.
524
+ // - Patch shape but unmergeable → blocked + revision-patch-unmergeable
525
+ // (no retryable loop).
526
+ let planJsonValue = null;
477
527
  try {
478
- originalValue = extractFrontendImplementationJson(originalText);
479
- patchValue = extractFrontendImplementationJson(planText);
528
+ planJsonValue = extractFrontendImplementationJson(planText);
480
529
  }
481
- catch (error) {
482
- return finalizePrewrite(input, {
483
- ...basePending,
484
- verdict,
485
- classification: "retryable-invalid",
486
- failureReason: `frontend prewrite gate revisionPatch mode failed to parse contract/patch: ${error instanceof Error ? error.message : String(error)}`,
487
- failureCode: "contract-invalid",
488
- });
530
+ catch {
531
+ // Not machine-readable JSON: fall back to the legacy full-contract
532
+ // path where analyzeFrontendImplementationContract surfaces the
533
+ // original diagnostic (fail-closed without a new failure mode).
489
534
  }
490
- let merged;
491
- try {
492
- merged = applyFrontendImplementationContractPatch(originalValue, patchValue);
493
- }
494
- catch (error) {
495
- return finalizePrewrite(input, {
496
- ...basePending,
497
- verdict,
498
- classification: "retryable-invalid",
499
- failureReason: `frontend prewrite gate revisionPatch apply failed: ${error instanceof Error ? error.message : String(error)}`,
500
- failureCode: "contract-invalid",
501
- });
535
+ if (planJsonValue !== null && !isFullContractShape(planJsonValue)) {
536
+ const originalNodeId = input.config.planFallbackFromNodeIds[0];
537
+ if (!originalNodeId || originalNodeId === planNodeId) {
538
+ return finalizePrewrite(input, {
539
+ ...basePending,
540
+ verdict,
541
+ classification: "blocked",
542
+ failureReason: "frontend prewrite gate: the effective plan output is a merge-patch delta but no distinct original contract node is available. Check the DAG revisionPatch/planFallbackFromNodeIds configuration or regenerate the DAG.",
543
+ failureCode: "revision-patch-unmergeable",
544
+ });
545
+ }
546
+ try {
547
+ const originalText = await readNodeText(input.runDir, originalNodeId);
548
+ const originalValue = extractFrontendImplementationJson(originalText);
549
+ if (!isFullContractShape(originalValue)) {
550
+ throw new Error(`original contract node ${originalNodeId} is not a full frontend implementation contract`);
551
+ }
552
+ effectiveContractText = await mergeRevisionPatch(input.runDir, planText, originalNodeId);
553
+ }
554
+ catch (error) {
555
+ return finalizePrewrite(input, {
556
+ ...basePending,
557
+ verdict,
558
+ classification: "blocked",
559
+ failureReason: `frontend prewrite gate: the effective plan output is a merge-patch delta but it cannot be merged onto the original contract node ${originalNodeId}: ${error instanceof Error ? error.message : String(error)}. Check the DAG revisionPatch/planFallbackFromNodeIds configuration or regenerate the DAG.`,
560
+ failureCode: "revision-patch-unmergeable",
561
+ });
562
+ }
502
563
  }
503
- effectiveContractText = serializeDeterministicJson(merged);
504
564
  }
505
565
  const requirementIdSearchText = effectiveContractText ?? planText;
506
566
  const missingIds = input.config.requiredRequirementIds.filter((id) => !requirementIdSearchText.includes(id));